import pandas as pd
import numpy as np
import random
import string
import uuid
import time
import datetime
import pytz
import base64
from faker import Faker
from faker.providers import BaseProvider
import ipaddress
import json
import hashlib
from urllib.parse import quote, unquote
import csv
import os
import sys
import ast
import math


# 设置随机种子，确保结果可复现
random.seed(42)
np.random.seed(42)

# 初始化Faker
fake = Faker()

# 国家IP地址池 (CIDR格式)
country_ip_ranges = {
    "China": ["113.0.0.0/8", "114.0.0.0/8", "115.0.0.0/8", "116.0.0.0/8", "117.0.0.0/8", "118.0.0.0/8", "119.0.0.0/8", "120.0.0.0/8", "121.0.0.0/8", "122.0.0.0/8", "123.0.0.0/8", "180.0.0.0/8", "182.0.0.0/8", "183.0.0.0/8"],
    "United States": ["72.0.0.0/8", "73.0.0.0/8", "74.0.0.0/8", "75.0.0.0/8", "76.0.0.0/8", "77.0.0.0/8", "78.0.0.0/8", "79.0.0.0/8", "80.0.0.0/8", "81.0.0.0/8", "82.0.0.0/8", "83.0.0.0/8", "84.0.0.0/8", "85.0.0.0/8", "86.0.0.0/8", "87.0.0.0/8", "88.0.0.0/8", "89.0.0.0/8", "90.0.0.0/8", "91.0.0.0/8", "92.0.0.0/8", "93.0.0.0/8", "94.0.0.0/8", "95.0.0.0/8", "96.0.0.0/8", "97.0.0.0/8", "98.0.0.0/8", "99.0.0.0/8"],
    "Russia": ["100.0.0.0/8", "101.0.0.0/8", "102.0.0.0/8", "103.0.0.0/8", "104.0.0.0/8", "105.0.0.0/8", "106.0.0.0/8", "107.0.0.0/8", "108.0.0.0/8", "109.0.0.0/8", "110.0.0.0/8", "111.0.0.0/8", "112.0.0.0/8", "200.0.0.0/8", "201.0.0.0/8"],
    "Japan": ["132.0.0.0/8", "133.0.0.0/8", "134.0.0.0/8", "135.0.0.0/8", "136.0.0.0/8", "137.0.0.0/8", "138.0.0.0/8", "139.0.0.0/8", "140.0.0.0/8", "141.0.0.0/8", "142.0.0.0/8", "143.0.0.0/8", "144.0.0.0/8", "145.0.0.0/8", "146.0.0.0/8"],
    "Germany": ["52.0.0.0/8", "53.0.0.0/8", "54.0.0.0/8", "55.0.0.0/8", "56.0.0.0/8", "57.0.0.0/8", "58.0.0.0/8", "59.0.0.0/8", "60.0.0.0/8", "61.0.0.0/8", "62.0.0.0/8", "63.0.0.0/8", "64.0.0.0/8", "65.0.0.0/8", "66.0.0.0/8"],
    "United Kingdom": ["25.0.0.0/8", "26.0.0.0/8", "27.0.0.0/8", "28.0.0.0/8", "29.0.0.0/8", "30.0.0.0/8", "31.0.0.0/8", "32.0.0.0/8", "33.0.0.0/8", "34.0.0.0/8", "35.0.0.0/8", "36.0.0.0/8", "37.0.0.0/8", "38.0.0.0/8", "39.0.0.0/8"],
    "France": ["40.0.0.0/8", "41.0.0.0/8", "42.0.0.0/8", "43.0.0.0/8", "44.0.0.0/8", "45.0.0.0/8", "46.0.0.0/8", "47.0.0.0/8", "48.0.0.0/8", "49.0.0.0/8", "50.0.0.0/8", "51.0.0.0/8", "160.0.0.0/8", "161.0.0.0/8", "162.0.0.0/8"],
    "India": ["67.0.0.0/8", "68.0.0.0/8", "69.0.0.0/8", "70.0.0.0/8", "71.0.0.0/8", "150.0.0.0/8", "151.0.0.0/8", "152.0.0.0/8", "153.0.0.0/8", "154.0.0.0/8", "155.0.0.0/8", "156.0.0.0/8", "157.0.0.0/8", "158.0.0.0/8", "159.0.0.0/8"],
    "South Korea": ["163.0.0.0/8", "164.0.0.0/8", "165.0.0.0/8", "166.0.0.0/8", "167.0.0.0/8", "168.0.0.0/8", "169.0.0.0/8", "170.0.0.0/8", "171.0.0.0/8", "172.0.0.0/8", "173.0.0.0/8", "174.0.0.0/8", "175.0.0.0/8", "176.0.0.0/8", "177.0.0.0/8"],
    # "Brazil": ["178.0.0.0/8", "179.0.0.0/8", "181.0.0.0/8", "184.0.0.0/8", "185.0.0.0/8", "186.0.0.0/8", "187.0.0.0/8", "188.0.0.0/8", "189.0.0.0/8", "190.0.0.0/8", "191.0.0.0/8", "192.0.0.0/8", "193.0.0.0/8", "194.0.0.0/8", "195.0.0.0/8"],
    # "Australia": ["210.0.0.0/8", "211.0.0.0/8", "212.0.0.0/8", "213.0.0.0/8", "214.0.0.0/8", "215.0.0.0/8", "216.0.0.0/8", "217.0.0.0/8", "218.0.0.0/8", "219.0.0.0/8", "220.0.0.0/8", "221.0.0.0/8", "222.0.0.0/8", "223.0.0.0/8", "224.0.0.0/8"],
    # "Canada": ["225.0.0.0/8", "226.0.0.0/8", "227.0.0.0/8", "228.0.0.0/8", "229.0.0.0/8", "230.0.0.0/8", "231.0.0.0/8", "232.0.0.0/8", "233.0.0.0/8", "234.0.0.0/8", "235.0.0.0/8", "236.0.0.0/8", "237.0.0.0/8", "238.0.0.0/8", "239.0.0.0/8"],
    # "Italy": ["8.0.0.0/8", "9.0.0.0/8", "10.0.0.0/8", "11.0.0.0/8", "12.0.0.0/8", "13.0.0.0/8", "14.0.0.0/8", "15.0.0.0/8", "16.0.0.0/8", "17.0.0.0/8", "18.0.0.0/8", "19.0.0.0/8", "20.0.0.0/8", "21.0.0.0/8", "22.0.0.0/8"],
    # "Spain": ["23.0.0.0/8", "24.0.0.0/8", "4.0.0.0/8", "5.0.0.0/8", "6.0.0.0/8", "7.0.0.0/8", "1.0.0.0/8", "2.0.0.0/8", "3.0.0.0/8", "240.0.0.0/8", "241.0.0.0/8", "242.0.0.0/8", "243.0.0.0/8", "244.0.0.0/8", "245.0.0.0/8"],
    # "Netherlands": ["246.0.0.0/8", "247.0.0.0/8", "248.0.0.0/8", "249.0.0.0/8", "250.0.0.0/8", "251.0.0.0/8", "252.0.0.0/8", "253.0.0.0/8", "254.0.0.0/8", "255.0.0.0/8", "196.0.0.0/8", "197.0.0.0/8", "198.0.0.0/8", "199.0.0.0/8", "202.0.0.0/8"]
}

# 国家时区偏移量（小时）
country_timezone_offsets = {
    'China': 8,
    'United States': -5,
    'Russia': 3,
    'Japan': 9,
    'Germany': 1,
    'United Kingdom': 0,
    'France': 1,
    'India': 5.5,
    'South Korea': 9,
    'Brazil': -3,
    'Australia': 10,
    'Canada': -5,
    'Italy': 1,
    'Spain': 1,
    'Netherlands': 1
}

cookie_counter = {}
cookie_base={}
ua_cache = {}

##############################################################################################################
class Attacker:
    def __init__(self, attacker_id, level, country, used_ip_prefixes,basenum,noise_flag):
        self.attacker_id = attacker_id
        self.noise_flag=noise_flag
        self.level = level
        self.country = country
        self.timezone_offset = country_timezone_offsets.get(country, 0)
        self.used_ip_prefixes = used_ip_prefixes  # 记录已使用的IP前缀
        self.ip_addresses = self.generate_ip_addresses()
        self.host_preferences = self.generate_host_preferences()
        self.attack_type_preferences = self.generate_attack_type_preferences()
        self.payload_location_preferences = self.generate_payload_location_preferences()
        self.encoding_preferences = self.generate_encoding_preferences()
        self.attack_frequency = self.generate_attack_frequency()
        self.time_preference = self.generate_time_preference()
        self.http_method_preferences = self.generate_http_method_preferences()
        self.payload_preferences = self.generate_payload_preferences()
        self.url_preferences = self.generate_url_preferences()
        self.geturl_preferences = self.generate_geturl_preferences()
        self.accept_language_preferences = self.generate_accept_language_preferences()
        self.x_forwarded_for_preferences = self.generate_x_forwarded_for_preference()
        self.post_body_preference=self.generate_post_body_preference()
        self.phase_preference=self.generate_phase_preference(basenum)
        self.reconnaissance_catalog=self.generate_reconnaissance_catalog()
        self.exploitation_catalog=self.generate_exploitation_catalog()
        self.noise_save=False

#-------ip_addresses
    def generate_ip_addresses(self):  #生成攻击者的IP地址池++++++++
        # 生成攻击者的IP地址池
        ips = []
        
        def get_random_ip_from_cidr(cidr):     #主机地址随机生成，网段地址从列表中随机抽取
            network = ipaddress.ip_network(cidr, strict=False)
            host_bits = 32 - network.prefixlen
            random_host = random.getrandbits(host_bits)
            return str(network.network_address + random_host)

        def judge_ip_prefixes(ip):   #判断ip地址的前三位是否重复
            prefix = '.'.join(ip.split('.')[:3])
            if prefix not in self.used_ip_prefixes:
                self.used_ip_prefixes.append(prefix)    
                return True
            else:
                return False

        def random_gauss_ip(ip,std):     #服从高斯分布的随机数选择，ip为均值，std为标准差
            value = int(round(random.gauss(ip, std)))
            if value < 1:
                value = 1
            elif value > 254:
                value = 254
            return value
        
        if self.noise_flag==True:   # 噪声：随机生成ip,有概率两个地址
            noise_n=1
            # if random.random() < 0.2:
            #     noise_n=2
            
            for _ in range(noise_n): #噪声ip数量
                cidr = random.choice(country_ip_ranges[self.country])
                base_ip = get_random_ip_from_cidr(cidr)
                while not judge_ip_prefixes(base_ip):
                    base_ip = get_random_ip_from_cidr(cidr)
                ips.append(base_ip)
        

        if self.level == "初级" and self.noise_flag==False:
            # 初级：从国家IP池中选择基础IP
            cidr = random.choice(country_ip_ranges[self.country])
            base_ip = get_random_ip_from_cidr(cidr)
            while not judge_ip_prefixes(base_ip):
                base_ip = get_random_ip_from_cidr(cidr)
            ips.append(base_ip)
            
            # 可能有个相似IP（同一网段）
            for _ in range(random.randint(2, 15)):  #初级随机ip数量++++++++++++++++++++++++++++++++++++++++++++++++
                parts = base_ip.split('.')
                parts[-1] = str(random.randint(1, 254))
                new_ip='.'.join(parts)
                while new_ip in ips:
                    parts[-1] = str(random.randint(1, 254))
                    new_ip='.'.join(parts)
                ips.append(new_ip)

        elif self.level == "中级" and self.noise_flag==False:
            # 中级：同一国家的不同地址
            cidrs = country_ip_ranges[self.country]
            cidr = random.choice(cidrs)
            base_ip = get_random_ip_from_cidr(cidr)
            while not judge_ip_prefixes(base_ip):
                base_ip = get_random_ip_from_cidr(cidr)

            
            if self.attacker_id==1:
                base_medium_num=15
            else:
                base_medium_num=15

            for _ in range(random.randint(base_medium_num, base_medium_num+10)):  #中级随机ip数量+++++++++++++++++++++++++++++++++++++++++++  15  25
                parts = base_ip.split('.')
                parts[1] = str(random_gauss_ip(int(parts[1]), 10))
                new_ip='.'.join(parts)
                while not judge_ip_prefixes(new_ip):
                    parts[1] = str(random_gauss_ip(int(parts[1]), 10))
                    new_ip='.'.join(parts)
                ips.append(new_ip)
                if random.random() < 0.75:
                    parts = new_ip.split('.')
                    parts[-2] = str(random_gauss_ip(int(parts[-2]), 20))
                    new_ip='.'.join(parts)
                    while not judge_ip_prefixes(new_ip):
                        parts[-2] = str(random_gauss_ip(int(parts[-2]), 20))
                        new_ip='.'.join(parts)
                    ips.append(new_ip)

        elif self.level == "高级" and self.noise_flag==False:  # 高级
            # 高级：不同国家的IP
            selected_countries = random.sample(list(country_ip_ranges.keys()), k=random.randint(2, 3))  # 随机选择2-3个国家
            all_cidrs = []
            for country in selected_countries:
                all_cidrs.extend(country_ip_ranges[country])
            # 高级：基于基础IP生成相似IP，模仿中级攻击者模式
            for country in selected_countries:
                cidrs = country_ip_ranges[country]
                cidr = random.choice(cidrs)
                base_ip = get_random_ip_from_cidr(cidr)
                while not judge_ip_prefixes(base_ip):
                    base_ip = get_random_ip_from_cidr(cidr)
                # ips.append(base_ip)
                
                # 生成2-3个相似IP（修改第二段和倒数第二段）
                for _ in range(random.randint(15, 20)):  #高级随机ip数量+++++++++++++++++++++++++++++++++++++++++++++++++
                    parts = base_ip.split('.')
                    # 随机修改第二段或倒数第二段
                    if random.random() < 0.5:
                        parts[1] = str(random_gauss_ip(int(parts[1]), 30))  # 较小标准差，保持更高相似性
                    else:
                        parts[-2] = str(random_gauss_ip(int(parts[-2]), 60))
                    new_ip = '.'.join(parts)
                    while not judge_ip_prefixes(new_ip):
                        if random.random() < 0.5:
                            parts[1] = str(random_gauss_ip(int(parts[1]), 30))
                        else:
                            parts[-2] = str(random_gauss_ip(int(parts[-2]), 60))
                        new_ip = '.'.join(parts)
                    ips.append(new_ip)

        ips = sorted(ips, key=lambda ip: tuple(map(int, ip.split('.'))))
        
        # print('ips',ips)
        # print('ips2',ips2)
        return ips

#-------host_preferences
    def generate_host_preferences(self):  #生成攻击者偏好的host池+++++++++
        # 生成攻击者偏好的host
        hosts = []
        num_hosts = 0
        # 预设主机名列表
        HOST_LIST = [
            "example.com", "test.com", "sample.net", "demo.org", "example.net", "test.org", "sample.com", "demo.net",
            "site.example", "service.test", "data.sample", "app.demo","example.io", "test.info", "sample.co", "demo.biz",
            "web.example", "api.test", "cloud.sample", "server.demo",  "mail.example", "tools.test", "docs.sample", "code.demo",
            "example.xyz", "test.dev", "sample.cc", "demo.ws","forum.example", "store.test",
            "blog.example", "shop.test","example.co", "test.net", "sample.org", "demo.com",
            "example.info", "test.biz", "sample.io", "demo.xyz", "www.example", "admin.test", "files.sample", "client.demo",
            "example.dev", "test.ws", "sample.xyz", "demo.info","ftp.example", "db.test", "storage.sample", "client.demo",
            # "news.example", "shop.test", "docs.sample", "code.demo", "example.biz", "test.io", "sample.dev", "demo.cc",
            # "docs.example", "code.test", "app.sample", "data.demo", "net.example", "org.test", "com.sample", "biz.demo",
            # "mail.test", "web.sample", "api.demo", "cloud.example", "server.test", "site.sample", "service.demo", "tools.example",
            # "www.test", "admin.sample", "files.demo", "ftp.test", "db.sample", "storage.demo", "news.test", "forum.sample",
            # "store.demo", "blog.sample", "shop.demo", "example.ws", "test.cc", "sample.biz", "demo.io", "example.org",
            # "test.net", "sample.com", "demo.co", "example.biz", "test.io", "sample.dev", "demo.cc", "example.info",
            # "test.xyz", "sample.ws", "demo.dev", "example.store", "test.forum", "sample.blog", "demo.shop", "example.client",
            # "test.storage", "sample.db", "demo.ftp", "example.news", "test.code", "sample.docs", "demo.tools"
        ]

        if self.level == "初级":
            # 初级：1-2个host
            num_hosts = random.randint(2, 4)           #+++++++++++++++++++++++++++++++++++
        elif self.level == "中级":
            # 中级：2-3个host
            num_hosts = random.randint(4, 6)            #+++++++++++++++++++++++++++++++
        else:
            # 高级：3-4个host,可能伪造
            num_hosts = random.randint(5, 8)              #+++++++++++++++++++++++++++++++

        for _ in range(num_hosts):
            if self.level == "高级" and random.random() < 0.1:
                # 伪造的主机名
                hosts.append(f'fake-{uuid.uuid4().hex[:8]}.com')
            else:
                # 从预设列表中随机选择主机名
                hosts.append(random.choice(HOST_LIST))

        return hosts

#-------attack_type
    def generate_attack_type_preferences(self): #生成攻击者偏好的攻击类型
        base_probs = []

        if self.level == "初级":
            base_probs=[0.3, 0.3, 0.1, 0, 0, 0.2, 0.2, 0, 0, 0.1]
        elif self.level == "中级":
            base_probs=[0.15, 0.15, 0.15, 0.1, 0.1, 0.2, 0.2, 0.05, 0.05, 0.05]
        else:  # 高级
            base_probs=[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.2]

        # 随机选择 2 到 3 个索引
        num_changes = random.randint(2, 4)
        if self.level == "初级":
            indices_to_increase = random.sample([0,1,2,5,6,9], num_changes)
        else:
            indices_to_increase = random.sample(range(len(base_probs)), num_changes)

        # 对选中的索引进行增加操作
        for idx in indices_to_increase:
            increase_value = random.uniform(0.6, 0.9)
            base_probs[idx] += increase_value

        total = sum(base_probs)
        normalized = [p / total for p in base_probs]
        base_probs=normalized

        return base_probs

    def generate_reconnaissance_catalog(self,default_list=[]):
        # 原始列表
        if default_list == []:
            original_list = ["DirectoryEnumeration", "InformationDisclosure", "WebVulnerabilityScan"]
        else:
            original_list = default_list

        probs_to_shuffle = self.attack_type_preferences[:3]
        # 生成一个索引列表，并打乱顺序
        indices = list(range(len(original_list)))
        random.shuffle(indices)
        # 根据打乱的索引调整 original_list 和 probs_to_shuffle
        shuffled_list = [original_list[i] for i in indices]
        shuffled_probs = [probs_to_shuffle[i] for i in indices]

        # 将调整后的概率值放回 base_probs 中
        self.attack_type_preferences[:3] = shuffled_probs
        return shuffled_list

    def generate_exploitation_catalog(self,default_list=[]):
        # 原始列表
        if default_list == []:
            original_list = ["OsCommanding", "PathTransversal", "SQLInjection", "XSS", "CSRF", "ServerSideInclude"]
        else:
            original_list = default_list
        # probs_to_shuffle = self.attack_type_preferences[3:9]
        # # 生成一个索引列表，并打乱顺序
        # indices = list(range(len(original_list)))
        # random.shuffle(indices)
        # # 根据打乱的索引调整 original_list 和 probs_to_shuffle
        # shuffled_list = [original_list[i] for i in indices]
        # shuffled_probs = [probs_to_shuffle[i] for i in indices]

        # # 将调整后的概率值放回 base_probs 中
        # self.attack_type_preferences[3:9] = shuffled_probs

        # 提取对应的概率子列表
        corresponding_probs = self.attack_type_preferences[3:9]  # [0.1, 0.1, 0.1, 0.1, 0.1, 0.1]

        # 将两个列表配对
        paired = list(zip(original_list, corresponding_probs))

        # 打乱配对列表
        random.shuffle(paired)

        # 拆分回两个列表
        shuffled_list, shuffled_probs = zip(*paired)
        # print('原始',original_list)
        # print('原始',self.attack_type_preferences[3:9])

        # 转换为 list（因为 zip 返回的是 tuple）
        original_list[:] = list(shuffled_list)
        self.attack_type_preferences[3:9] = list(shuffled_probs)

        # print('打乱',original_list)
        # print('打乱',self.attack_type_preferences[3:9])


        return list(shuffled_list)

    def generate_attack_type(self, phase=None,phase_count=None,index=None): #根据攻击阶段和偏好生成攻击类型
        base_probs=self.attack_type_preferences

        if phase == "reconnaissance":
            probs=base_probs[:3]
            normalized_probs = [prob / sum(probs) for prob in probs]
            new_index=0
            new_sum=0

            for i in range(len(normalized_probs)):
                new_sum+=normalized_probs[i]  # 计算累计概率
                if new_sum >= index/phase_count:   # 计算当前索引对应的累计概率
                    new_index=i
                    break
            return self.reconnaissance_catalog[new_index]

        elif phase == "exploitation":
            probs=base_probs[3:9]
            normalized_probs = [prob / sum(probs) for prob in probs]
            new_index=0
            new_sum=0

            for i in range(len(normalized_probs)):
                new_sum+=normalized_probs[i]
                if new_sum >= index/phase_count:
                    new_index=i
                    break
            return self.exploitation_catalog[new_index]
        else:
            return "WebshellCommunication"   #persistence

#-------payload_location
    def generate_payload_location_preferences(self):  #生成攻击者偏好的载荷位置
        # 生成攻击者偏好的载荷位置
        locations = ["URL", "User Agent", "Accept Language", "Cookie", "X-Forwarded-For", "Post Body"]
        base_probs = {}

        if self.level == "初级":
            base_probs = {
                "URL": 0.7, "User Agent": 0.00, "Accept Language": 0.00,
                "Cookie": 0.0, "X-Forwarded-For": 0.0, "Post Body": 0.3
            }
            # 添加个体差异
            base_probs["URL"] = 0.7 * random.uniform(0.5, 3)
            base_probs["Post Body"]=0.3 * random.uniform(0.5, 3)
            # 重新归一化
            total = sum(base_probs.values())
            for loc in base_probs:
                base_probs[loc] /= total
            return base_probs
        elif self.level == "中级":
            base_probs = {
                "URL": 0.7, "User Agent": 0.1, "Accept Language": 0.00,
                "Cookie": 0.05, "X-Forwarded-For": 0.0, "Post Body": 0.5
            }
        else:  # 高级
            base_probs = {
                "URL": 0.7, "User Agent": 0.15, "Accept Language": 0.15,
                "Cookie": 0.15, "X-Forwarded-For": 0.15, "Post Body": 0.5
            }

        # 添加个体偏好
        preferred_locations = random.sample(locations, random.randint(2, 3))
        for loc in preferred_locations:
            base_probs[loc] = base_probs[loc]*random.uniform(0.5, 3)

        # 归一化概率
        total = sum(base_probs.values())
        for loc in base_probs:
            base_probs[loc] /= total

        return base_probs

    def get_random_payload_location(self, http_method):  #根据HTTP方法和偏好获取随机载荷位置
        # 根据偏好获取随机载荷位置   
        locations = list(self.payload_location_preferences.keys())
        probs = list(self.payload_location_preferences.values())
        location=np.random.choice(locations, p=probs)
        while location=="Post Body" and http_method not in ["POST", "PUT", "PATCH"]:
            location=np.random.choice(locations, p=probs)
        return location

#-------http_method
    def generate_http_method_preferences(self):  #生成HTTP方法偏好
        # 生成HTTP方法偏好
        methods = ["GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "PATCH"]
        probs = {}

        if self.level == "初级":
            probs = {"GET": 0.9, "POST": 0.1}

            for m in methods:
                if m not in probs:
                    probs[m] = 0
            # 添加一些随机性
            preferred_methods = random.sample(["GET", "POST"], random.randint(1, 1))
            for m in preferred_methods:
                probs[m] += random.uniform(0.9, 1.5)
            # 归一化
            total = sum(probs.values())
            for m in probs:
                probs[m] /= total
        elif self.level == "中级":
            probs = {"GET": 0.4, "POST": 0.4, "PUT": 0.1, "DELETE": 0.1}
            for m in methods:
                if m not in probs:
                    probs[m] = 0
            # 添加一些随机性
            preferred_methods = random.sample(["GET", "POST", "PUT", "DELETE"], random.randint(1, 2))
            for m in preferred_methods:
                probs[m] += random.uniform(1, 1.5)
            # 归一化
            total = sum(probs.values())
            for m in probs:
                probs[m] /= total
        else:  # 高级
            # 更均衡的分布
            probs = {}
            for m in methods:
                probs[m] = 1/len(methods)
            # 添加一些随机性
            preferred_methods = random.sample(methods, random.randint(2, 3))
            for m in preferred_methods:
                probs[m] += random.uniform(1.2, 1.7)
            # 归一化
            total = sum(probs.values())
            for m in probs:
                probs[m] /= total

        return probs

    def get_random_http_method(self):  #根据HTTP方法偏好获取随机HTTP方法
        # 根据偏好获取随机HTTP方法
        methods = list(self.http_method_preferences.keys())
        probs = list(self.http_method_preferences.values())
        return np.random.choice(methods, p=probs)

#-------user_agent   
    def generate_user_agent(self, attacker_id, attack_type):   #初级可能重复，中级小概率重复，高级不重复  每个攻击者每个攻击类型对应一个UA
        attacker_level=self.level
        # # 初始化UA缓存字典，如果不存在
        # if not hasattr(self, 'ua_cache'):
        #     self.ua_cache = {}

        # if attacker_id==-1:
        #     return fake.firefox()


        # 为每个攻击者创建唯一键
        key = f"{attacker_id}_{attack_type}"  #每个攻击者每个攻击类型对应一个UA   此时的attacker_id其实是linshi_id


        # 检查缓存中是否已有UA
        if key in ua_cache:
            return ua_cache[key]

        # 工具型UA ，与攻击方式相对应
        tool_ua_map = {
            'sql_injection': [
                'sqlmap/1.6.12#stable (http://sqlmap.org   )',
                'sqlmap/1.7.2 (http://sqlmap.org   )',
                'sqlmap/1.7.3 (http://sqlmap.org   )',
                'sqlmap/1.7.4 (http://sqlmap.org   )',
                'sqlmap/1.7.5#stable (http://sqlmap.org   )',
                'sqlmap/1.8.0 (http://sqlmap.org   )',
                'sqlmap/1.8.1 (http://sqlmap.org   )',
                'sqlmap/1.8.2 (http://sqlmap.org   )',
                'sqlmap/1.8.3 (http://sqlmap.org   )',
                'sqlmap/1.8.4#stable (http://sqlmap.org   )',
                'sqlmap/1.9.0 (http://sqlmap.org   )',
                'sqlmap/1.9.1 (http://sqlmap.org   )',
                'sqlmap/1.9.2 (http://sqlmap.org   )',
                'sqlmap/1.9.3 (http://sqlmap.org   )',
                'sqlmap/1.9.4#stable (http://sqlmap.org   )',
                'sqlmap/2.0.0 (http://sqlmap.org   )',
                'sqlmap/2.0.1 (http://sqlmap.org   )',
                'sqlmap/2.0.2 (http://sqlmap.org   )',
                'sqlmap/2.0.3 (http://sqlmap.org   )',
                'sqlmap/2.0.4#stable (http://sqlmap.org   )',
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
                'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Firefox/125.0',
                'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1',
                'curl/7.88.1',
                'Python-requests/2.31.0',
                'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.179 Mobile Safari/537.36',
                'Googlebot/2.1 (+http://www.google.com/bot.html)',
                'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0',
                'Mozilla/5.0 (iPad; CPU OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1',
                'bingbot/2.0 (+http://www.bing.com/bingbot.htm)',
                'Applebot/0.3 (http://www.apple.com/go/applebot)',
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0',
                'Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)',
                'Nmap Scripting Engine (http://nmap.org   )',
                'PostmanRuntime/7.38.0',
                'axios/1.6.8',
                'java-http-client/11.0.15',
                'Microsoft-WebDAV-MiniRedir/10.0.19041',
                'Go-http-client/1.1',
                'Wget/1.21.3'
            ],
            'xss': [
                'BurpSuite/2023.9.1',
                'BurpSuite/2024.2',
                'BurpSuite/2024.3',
                'BurpSuite/2024.4',
                'BurpSuite/2024.5',
                'BurpSuite/2024.6',
                'BurpSuite/2024.7',
                'BurpSuite/2024.8',
                'BurpSuite/2024.9',
                'BurpSuite/2024.10',
                'BurpSuite/2024.11',
                'BurpSuite/2024.12',
                'BurpSuite/2025.1',
                'BurpSuite/2025.2',
                'BurpSuite/2025.3',
                'BurpSuite/2025.4',
                'BurpSuite/2025.5',
                'BurpSuite/2025.6',
                'BurpSuite/2025.7',
                'BurpSuite/2025.8',
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
                'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Firefox/125.0',
                'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1',
                'curl/7.88.1',
                'Python-requests/2.31.0',
                'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.179 Mobile Safari/537.36',
                'Googlebot/2.1 (+http://www.google.com/bot.html)',
                'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0',
                'Mozilla/5.0 (iPad; CPU OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1',
                'bingbot/2.0 (+http://www.bing.com/bingbot.htm)',
                'Applebot/0.3 (http://www.apple.com/go/applebot)',
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0',
                'Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)',
                'Nmap Scripting Engine (http://nmap.org   )',
                'PostmanRuntime/7.38.0',
                'axios/1.6.8',
                'java-http-client/11.0.15',
                'Microsoft-WebDAV-MiniRedir/10.0.19041',
                'Go-http-client/1.1',
                'Wget/1.21.3'
            ],
            'csrf': [
                'OWASP ZAP/2.13.0',
                'OWASP ZAP/2.14.0',
                'OWASP ZAP/2.14.1',
                'OWASP ZAP/2.14.2',
                'OWASP ZAP/2.15.0',
                'OWASP ZAP/2.15.1',
                'OWASP ZAP/2.15.2',
                'OWASP ZAP/2.15.3',
                'OWASP ZAP/2.16.0',
                'OWASP ZAP/2.16.1',
                'OWASP ZAP/2.16.2',
                'OWASP ZAP/2.17.0',
                'OWASP ZAP/2.17.1',
                'OWASP ZAP/2.17.2',
                'OWASP ZAP/2.17.3',
                'OWASP ZAP/2.18.0',
                'OWASP ZAP/2.18.1',
                'OWASP ZAP/2.18.2',
                'OWASP ZAP/2.19.0',
                'OWASP ZAP/2.19.1',
                'BurpSuite/2023.8.1',
                'BurpSuite/2023.9.2',
                'Acunetix/14.5',
                'Acunetix/14.6',
                'Nessus/10.5.2',
                'Nessus/10.6.0',
                'Netsparker/6.1',
                'Netsparker/6.4',
                'QualysGuard/10.3',
                'QualysGuard/10.6',
                'AppScan/10.0',
                'AppScan/10.3',
                'W3af/1.2.2',
                'W3af/1.2.5',
                'Nikto/2.4.9',
                'Nikto/2.5.2',
                'XSSer/1.8.1',
                'XSSer/1.8.4',
                'sqlmap/1.6.11',
                'sqlmap/2.0.5',
                 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
                'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Firefox/125.0',
                'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1',
                'curl/7.88.1',
                'Python-requests/2.31.0',
                'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.179 Mobile Safari/537.36',
                'Googlebot/2.1 (+http://www.google.com/bot.html)',
                'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0',
                'Mozilla/5.0 (iPad; CPU OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1',
                'bingbot/2.0 (+http://www.bing.com/bingbot.htm)',
                'Applebot/0.3 (http://www.apple.com/go/applebot)',
                'PostmanRuntime/7.38.0',
                'axios/1.6.8',
                'java-http-client/11.0.15',
                'Microsoft-WebDAV-MiniRedir/10.0.19041',
                'Go-http-client/1.1',
                'Wget/1.21.3',
                'Nmap Scripting Engine (http://nmap.org   )',
                'http-kit/0.7.0',
                'node-fetch/3.3.2',
                'Apache-HttpClient/4.5.14'
                ],
            'command_injection': [
                'curl/7.88.1',
                'curl/8.4.0',
                'curl/8.4.1',
                'curl/8.5.0',
                'curl/8.5.1',
                'curl/8.6.0',
                'curl/8.6.1',
                'curl/8.7.0',
                'curl/8.7.1',
                'curl/8.7.2',
                'curl/8.8.0',
                'curl/8.8.1',
                'curl/8.9.0',
                'curl/8.9.1',
                'curl/8.10.0',
                'curl/8.10.1',
                'curl/8.11.0',
                'curl/8.11.1',
                'curl/8.12.0',
                'curl/8.12.1',
                'curl/7.64.1',
                'curl/7.71.1',
                'curl/7.79.1',
                'curl/8.0.0',
                'curl/8.1.0',
                'curl/8.2.1',
                'curl/8.3.0',
                'curl/8.4.2',
                'curl/8.5.2',
                'curl/8.6.2',
                'Wget/1.21.3',
                'Wget/1.20.3',
                'Wget/1.19.5',
                'Wget/1.18.0',
                'Wget/1.16.3',
                'Python-requests/2.31.0',
                'Python-requests/2.28.1',
                'Python-requests/2.25.1',
                'Python-urllib/3.11',
                'Python-urllib/3.10',
                'Go-http-client/1.1',
                'Go-http-client/2.0',
                'HttpTool/1.0',
                'Nmap Scripting Engine (http://nmap.org   )',
                'Metasploit-RPC/6.3.0',
                'sqlmap/2.0.4#stable (http://sqlmap.org   )',
                'sqlmap/1.8.4#stable (http://sqlmap.org   )',
                'PostmanRuntime/7.38.0',
                'PostmanRuntime/7.29.2',
                'axios/1.6.8',
                'axios/0.27.2',
                'node-fetch/3.3.2',
                'fetch',
                'Apache-HttpClient/4.5.14',
                'Apache-HttpAsyncClient/4.1.4',
                'java-http-client/11.0.15',
                'java-http-client/17.0.8',
                'Microsoft-WebDAV-MiniRedir/10.0.19041',
                'Microsoft-CryptoAPI/6.1',
                'PowerShell/7.3.6',
                'PowerShell/5.1.19041',
                'OpenSSL/3.0.2',
                'OpenSSL/1.1.1w',
                'test-tool/v1.0',
                'custom-scraper/0.1',
                'automation-client/2.3',
                'httpie/3.2.1',
                'http-kit/0.7.0',
                'RestSharp/108.5.2.0',
                'Thunder Client',
                'insomnia/2023.5.8',
                'Paw/4.2.3',
                'curl/8.13.0',
                'curl/8.14.0'
            ],
            'default': [
                    'Python-urllib/3.10',
                    'Python-urllib/3.11',
                    'Python-urllib/3.11.1',
                    'Python-urllib/3.11.2',
                    'Python-urllib/3.11.3',
                    'Python-urllib/3.11.4',
                    'Python-urllib/3.12.0',
                    'Python-urllib/3.12.1',
                    'Python-urllib/3.12.2',
                    'Python-urllib/3.12.3',
                    'Python-urllib/3.12.4',
                    'Python-urllib/3.13.0',
                    'Python-urllib/3.13.1',
                    'Python-urllib/3.13.2',
                    'Python-urllib/3.13.3',
                    'Python-urllib/3.14.0',
                    'Python-urllib/3.14.1',
                    'Python-urllib/3.14.2',
                    'Python-urllib/3.14.3',
                    'Python-urllib/3.15.0',
                    'Python-urllib/3.9.18',
                    'Python-urllib/3.8.10',
                    'Python-urllib/3.7.16',
                    'Python-requests/2.31.0',
                    'Python-requests/2.28.1',
                    'Python-requests/2.25.1',
                    'Python-requests/2.20.0',
                    'curl/7.88.1',
                    'curl/8.4.0',
                    'curl/8.12.0',
                    'curl/8.14.0',
                    'Wget/1.21.3',
                    'Wget/1.20.3',
                    'Go-http-client/1.1',
                    'Go-http-client/2.0',
                    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
                    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Firefox/125.0',
                    'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
                    'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1',
                    'Mozilla/5.0 (iPad; CPU OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1',
                    'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.179 Mobile Safari/537.36',
                    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0',
                    'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0',
                    'Googlebot/2.1 (+http://www.google.com/bot.html)',
                    'bingbot/2.0 (+http://www.bing.com/bingbot.htm)',
                    'Applebot/0.3 (http://www.apple.com/go/applebot)',
                    'DuckDuckBot/1.1; (+http://duckduckgo.com/duckduckbot.html)',
                    'YandexBot/3.0 (+http://yandex.com/bots)',
                    'PostmanRuntime/7.38.0',
                    'PostmanRuntime/7.29.2',
                    'axios/1.6.8',
                    'axios/0.27.2',
                    'node-fetch/3.3.2',
                    'fetch',
                    'http-kit/0.7.0',
                    'Apache-HttpClient/4.5.14',
                    'java-http-client/11.0.15',
                    'Microsoft-WebDAV-MiniRedir/10.0.19041',
                    'PowerShell/7.3.6',
                    'insomnia/2023.5.8',
                    'Thunder Client',
                    'httpie/3.2.1',
                    'custom-scraper/1.0',
                    'automation-tool/0.5',
                    'test-client/v1'
                ]
            }
        
        # 浏览器UA
        browser_ua = [
                fake.chrome(), fake.firefox(), fake.safari(),
                fake.opera(), fake.internet_explorer()
            ]

        # 根据攻击者水平生成User Agent
        if attacker_level == "初级":
            # 选择与攻击方式对应的UA，如果没有对应的，则使用默认值
            ua_list = tool_ua_map.get(attack_type.lower(), tool_ua_map['default'])
            ua = random.choice(ua_list)

        elif attacker_level == "中级":
            # 工具或浏览器UA
            # 50%概率使用工具UA，50%概率使用浏览器UA
            if random.random() < 0.5:
                ua_list = tool_ua_map.get(attack_type.lower(), tool_ua_map['default'])
                ua = random.choice(ua_list)
            else:
                ua = random.choice(browser_ua)

        else:  # 高级
            # 伪装的UA字符串
            if random.random() < 0.8:
                # 多样化的浏览器UA模拟正常用户行为
                browsers = [fake.chrome, fake.firefox, fake.safari, fake.opera, fake.internet_explorer]
                ua = random.choice(browsers)()
            elif random.random() < 0.3:
                return fake.user_agent()
                # 伪装成爬虫
                # return random.choice([
                #     'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
                #     'Baiduspider/2.0 (+http://www.baidu.com/search/spider.html)',
                #     'Mozilla/5.0 (compatible; Bingbot/2.0; +http://www.bing.com/bingbot.htm)',
                #     'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)'
                # ])
            else:
                # 伪装成API客户端

                # 常见API客户端标识关键词
                api_client_keywords = [
                    "api-client", "rest-client", "http-client", 
                    "requests", "curl", "postman", "insomnia",
                    "api-explorer", "swagger", "openapi"
                ]
                """生成类似API客户端的User-Agent"""
                # 随机选择一个客户端类型
                client_type = random.choice(api_client_keywords)
                
                # 生成版本号 (x.y.z 格式)
                version = f"{random.randint(1, 3)}.{random.randint(0, 10)}.{random.randint(0, 50)}"
                
                # 随机添加平台信息
                platforms = ["(Linux)", "(Windows)", "(macOS)", "(Android)", "(iOS)", ""]
                platform = random.choice(platforms)
                
                # 构造API客户端风格的UA
                if platform:
                    return f"{client_type}/{version} {platform}"
                else:
                    return f"{client_type}/{version}"

                # return random.choice([
                #     'curl/7.68.0',
                #     'Wget/1.21.1',
                #     'PostmanRuntime/7.28.0',
                #     'Python-urllib/3.9'
                # ])

        # 缓存UA
        ua_cache[key] = ua
        return ua

#-------cookie
    def generate_cookie(self, host, attacker_id,phase):   #噪音的cookie每次攻击都随机生成，没有固定的cookie值，是否合理？+++++

        # if attacker_id==-1:  # 噪声未指定攻击者
        #     aa=random.randint(100,200)
        #     cookie_hash = hashlib.md5(f'{aa}_{host}'.encode()).hexdigest()[:16]
        #     return f'session={cookie_hash}; user=guest'

        attacker_level=self.level
        # 生成键，用于跟踪请求次数
        key = f'{attacker_id}_{host}_{phase}'       #每个攻击者每个host每个phase都有一个cookie  ,此时的attacker_id其实是linshi_id


        # 初始化计数器
        if key not in cookie_counter:
            cookie_counter[key] = 0
        # 增加计数
        else:
            cookie_counter[key] += 1

        if key not in cookie_base:
            cookie_base[key] = ''

        # 根据攻击者水平生成Cookie
        if attacker_level == "初级":
            # 不伪装，同一攻击者访问同一host的cookie值不变
            # 使用攻击者ID和host生成固定的cookie
            cookie_hash = hashlib.md5(f'{attacker_id}_{host}'.encode()).hexdigest()[:16]
            return f'session={cookie_hash}; user=guest'
        
        if attacker_level == "中级":
            if cookie_base[key] =='':
                cookie_hash = hashlib.md5(f'{attacker_id}_{host}_{phase}'.encode()).hexdigest()[:16]
                if random.random() < 0.3:
                    cookie_base[key]=f'session={cookie_hash}; user=guest'
                else:
                    cookie_base[key]=f'session={cookie_hash}; user=admin'
                return cookie_base[key]
            else:
                return cookie_base[key]

        if attacker_level == "高级":
            if cookie_base[key] =='':
                cookie_hash = hashlib.md5(f'{attacker_id}_{host}_{phase}'.encode()).hexdigest()[:16]
                if random.random() < 0.3:
                    cookie_base[key]=f'session={cookie_hash}; user=guest'
                else:
                    cookie_base[key]=f'session={cookie_hash}; user=admin'
                return cookie_base[key]
            else:
                return cookie_base[key]




        # elif attacker_level == "中级":
        #     # 每30次请求更换一次Cookie
        #     if  cookie_counter[key] % 40 == 0:
        #         # 更换Cookie
        #         if random.random() < 0.3:
        #             # 尝试修改关键字段
        #             cookie='session=' + hashlib.md5(f'{attacker_id}_{host}_{cookie_counter[key]//5}'.encode()).hexdigest()[:16] + '; role=admin'
        #             cookie_base[key]=cookie
        #             return cookie
        #         else:
        #             # 简单随机Cookie
        #             cookie='session=' + hashlib.md5(f'{attacker_id}_{host}_{cookie_counter[key]//5}'.encode()).hexdigest()[:16] + '; role=guest'
        #             cookie_base[key]=cookie
        #             return cookie
        #     else:
        #         # 保持之前的Cookie格式
        #         return cookie_base[key]


        # else:  # 高级
        #     # 每25次请求更换一次Cookie
        #     if cookie_counter[key] % 30 == 0:
        #         # 更换Cookie
        #         if random.random() < 0.5:
        #             # UUID格式
        #             cookie=f'session={str(uuid.uuid4())}; csrf_token={hashlib.sha256(str(uuid.uuid4()).encode()).hexdigest()[:32]}'
        #             cookie_base[key]=cookie
        #             return cookie
        #         else:
        #             # 模仿JSESSIONID格式
        #             cookie=f'JSESSIONID={hashlib.md5(str(uuid.uuid4()).encode()).hexdigest().upper()};'
        #             cookie_base[key]=cookie
        #             return cookie
        #     else:
        #         # 保持之前的Cookie格式，但添加一些变化
        #         return cookie_base[key]

#-------url_preferences
    def generate_url_preferences(self):    #需要调整，去掉参数！！！！！！！！++++++++++++++++++++
        # 基础URL路径
        simple_paths = [
            # 原始路径
            '/admin.php', '/login.jsp', '/index.php', '/search.php',
            '/user.php', '/product.php', '/page.php', '/view.php',
            '/home.php', '/logout.jsp', '/register.php', '/profile.php',
            '/settings.php', '/dashboard.jsp', '/about.php', '/contact.php',
            '/help.php', '/faq.jsp', '/terms.php', '/privacy.php',
            '/cart.php', '/checkout.jsp', '/order.php', '/payment.php',
            '/shipping.php', '/returns.jsp', '/search.jsp', '/results.php',
            '/list.php', '/detail.jsp', '/edit.php', '/delete.php',
            '/add.php', '/save.jsp', '/upload.php', '/download.jsp',
            '/export.php', '/import.jsp', '/backup.php', '/restore.jsp',
            '/logs.php', '/stats.jsp', '/report.php', '/analysis.php',
            '/config.php', '/setup.jsp', '/upgrade.php', '/install.jsp',
            '/uninstall.php', '/version.jsp',
            
            # 新增路径1 - 保持基础风格
            '/dashboard.php', '/account.jsp', '/portal.php', '/query.php',
            '/member.php', '/item.php', '/content.php', '/display.php',
            '/main.php', '/signout.jsp', '/signup.php', '/account.php',
            '/options.php', '/control.jsp', '/info.php', '/feedback.php',
            '/support.php', '/guide.jsp', '/conditions.php', '/policy.php',
            '/basket.php', '/purchase.jsp', '/transaction.php', '/billing.php',
            '/delivery.php', '/refunds.jsp', '/find.jsp', '/output.php',
            '/catalog.php', '/info.jsp', '/modify.php', '/remove.php',
            '/create.php', '/store.jsp', '/uploadify.php', '/get.jsp',
            '/extract.php', '/load.jsp', '/savebackup.php', '/recover.jsp',
            '/activity.php', '/metrics.jsp', '/summary.php', '/study.php',
            '/settings.php', '/initialize.jsp', '/update.php', '/setup.php',
            '/removeinstall.php', '/release.jsp',
            
            # # 新增路径2 - 保持基础风格
            # '/panel.php', '/signin.jsp', '/homepage.php', '/lookup.php',
            # '/customer.php', '/goods.php', '/section.php', '/show.php',
            # '/index.jsp', '/exit.php', '/enroll.jsp', '/profile.jsp',
            # '/preferences.jsp', '/overview.php', '/about.jsp', '/contact.jsp',
            # '/help.jsp', '/faqs.php', '/terms.jsp', '/privacy.jsp',
            # '/shoppingcart.jsp', '/checkout.php', '/order.jsp', '/payment.jsp',
            # '/shipment.php', '/returns.php', '/searchresults.php', '/findings.jsp',
            # '/directory.php', '/details.php', '/edit.jsp', '/delete.jsp',
            # '/insert.php', '/save.php', '/fileupload.php', '/download.php',
            # '/exportdata.jsp', '/importdata.php', '/backups.php', '/restores.php',
            # '/systemlogs.jsp', '/statistics.php', '/reports.jsp', '/analytics.jsp',
            # '/configuration.jsp', '/installsetup.php', '/upgrade.jsp', '/installer.php',
            # '/uninstall.jsp', '/version.php'
        ]

        # 中级路径
        intermediate_paths = [
            '/admin?test=1', '/admin/login', '/api/user', '/api/product',
            '/?page=home', '/category?id=1', '/post?id=123', '/file?name=test',
            '/admin?action=list', '/admin/logout', '/api/orders', '/api/cart',
            '/?page=about', '/category?name=books', '/post?slug=hello-world', '/file?type=image',
            '/user?username=test', '/user/profile', '/api/comments', '/api/reviews',
            '/?id=100', '/category?parent=2', '/post?author=5', '/file?size=1024',
            '/page?num=3', '/page/contact', '/api/tags', '/api/categories',
            '/?search=keyword', '/category?sort=asc', '/post?date=2023-01-01', '/file?download=1',
            '/dashboard?view=month', '/dashboard/stats', '/api/stats', '/api/analytics',
            '/?lang=en', '/category?filter=active', '/post?status=publish', '/file?folder=docs',
            '/settings?section=general', '/settings/security', '/api/settings', '/api/config',
            '/help?topic=login', '/help/faq', '/api/help', '/api/support',
            '/terms?version=2', '/terms/privacy', '/api/terms', '/api/privacy',
            '/admin?mode=edit', '/admin/settings', '/api/account', '/api/item',
            '/?view=profile', '/category?slug=electronics', '/post?status=draft', '/file?ext=pdf',
            '/admin?task=delete', '/admin/reset', '/api/payments', '/api/wishlist',
            '/?page=contact', '/category?parent=3', '/post?category=tech', '/file?folder=images',
            '/user?id=42', '/user/orders', '/api/ratings', '/api/subscriptions',
            '/?page=1', '/category?sort=desc', '/post?tag=tutorial', '/file?modified=2023',
            '/page?limit=10', '/page/about', '/api/items', '/api/tags',
            '/?query=example', '/category?status=active', '/post?featured=1', '/file?share=yes',
            '/dashboard?period=week', '/dashboard/data', '/api/metrics', '/api/reports',
            '/?locale=fr', '/category?type=physical', '/post?author=admin', '/file?access=public',
            '/settings?tab=profile', '/settings/privacy', '/api/preferences', '/api/options',
            '/help?section=account', '/help/support', '/api/guides', '/api/assistance',
            # '/terms?lang=en', '/terms/cookies', '/api/conditions', '/api/policies',
            # '/admin?page=users', '/admin/dashboard', '/api/session', '/api/order',
            # '/?id=profile', '/category?level=1', '/post?month=january', '/file?size=2048',
            # '/admin?action=add', '/admin/lock', '/api/invoices', '/api/coupons',
            # '/?page=products', '/category?count=10', '/post?year=2024', '/file?type=document',
            # '/user?email=test@example.com', '/user/settings', '/api/messages', '/api/notifications',
            # '/?item=5', '/category?order=name', '/post?comment=1', '/file?date=2023-05',
            # '/page?offset=20', '/page/privacy', '/api/cart', '/api/users',
            # '/?filter=active', '/category?search=phone', '/post?popular=1', '/file?protect=yes',
            # '/dashboard?user=admin', '/dashboard/logs', '/api/stats', '/api/analysis',
            # '/?country=us', '/category?include=inactive', '/post?edited=1', '/file?private=yes',
            # '/settings?group=security', '/settings/email', '/api/system', '/api/database',
            # '/help?search=password', '/help/manual', '/api/helpcenter', '/api/supportticket',
            # '/terms?updated=2024', '/terms/usage', '/api/agreement', '/api/confidentiality'
        ]

        # 高级路径 
        advanced_paths = [
            # 原始路径
            '/api/v1/users?token=abc123', '/rest/product/123/reviews',
            '/wp-json/wp/v2/posts', '/index.php?module=admin&action=edit',
            f'/random{random.randint(1000,9999)}.php', f'/{uuid.uuid4()}/endpoint',
            '/api/v2/products?api_key=xyz789&limit=20', '/rest/user/456/orders?status=active',
            '/wp-json/wp/v2/comments?post=789', '/admin.php?controller=user&method=delete&id=5',
            f'/random{random.randint(1000,9999)}.jsp', f'/{uuid.uuid4()}/resource',
            '/api/v3/orders?token=def456&sort=date', '/rest/category/789/products?page=2',
            '/wp-json/wp/v2/users?role=editor', '/index.php?module=product&action=view&id=12',
            f'/random{random.randint(1000,9999)}.asp', f'/{uuid.uuid4()}/data',
            '/api/v1/search?query=test&filter=active', '/rest/order/101/payments?method=credit',
            '/wp-json/wp/v2/pages?parent=5', '/page.php?section=news&item=latest&count=10',
            f'/random{random.randint(1000,9999)}.cgi', f'/{uuid.uuid4()}/service',
            '/api/v2/auth?client_id=123&client_secret=456', '/rest/review/202/ratings?min=4',
            '/wp-json/wp/v2/media?type=image', '/admin.php?tab=settings&subtab=security',
            f'/random{random.randint(1000,9999)}.pl', f'/{uuid.uuid4()}/api',
            '/api/v3/stats?period=month&group=user', '/rest/cart/303/items?format=json',
            '/wp-json/wp/v2/tags?search=tech', '/index.php?action=export&type=csv&date=2023',
            f'/random{random.randint(1000,9999)}.py', f'/{uuid.uuid4()}/endpoint',
            '/api/v1/logs?level=error&days=7', '/rest/user/404/permissions?role=admin',
            '/wp-json/wp/v2/settings?option=general', '/product.php?id=505&variant=red&size=l',
            f'/random{random.randint(1000,9999)}.rb', f'/{uuid.uuid4()}/resource',
            '/api/v2/backup?type=full&date=2023-06-01', '/rest/system/health?check=disk',
            '/wp-json/wp/v2/status?format=xml', '/admin.php?module=logs&action=view&date=today',
            '/api/v1/customers?auth_token=xyz789', '/rest/order/789/items',
            '/wp-json/wp/v2/media', '/index.php?controller=product&action=update&id=42',
            f'/random{random.randint(1000,9999)}.ashx', f'/{uuid.uuid4()}/api/v1/data',
            # '/api/v2/transactions?api_key=abc123&status=completed', '/rest/customer/123/addresses?type=shipping',
            # '/wp-json/wp/v2/users?per_page=10', '/admin.php?module=system&action=backup&type=full',
            # f'/random{random.randint(1000,9999)}.aspx', f'/{uuid.uuid4()}/v2/resource',
            # '/api/v3/products?token=xyz789&category=electronics', '/rest/product/456/reviews?rating=5',
            # '/wp-json/wp/v2/posts?status=publish', '/index.php?module=user&action=reset&user_id=78',
            # f'/random{random.randint(1000,9999)}.cfm', f'/{uuid.uuid4()}/data/v3',
            # '/api/v1/categories?search=books&limit=50', '/rest/payment/789/transactions?method=paypal',
            # '/wp-json/wp/v2/comments?approved=true', '/page.php?module=blog&featured=1&page=2',
            # f'/random{random.randint(1000,9999)}.do', f'/{uuid.uuid4()}/service/v1',
            # '/api/v2/sessions?client_id=456&client_secret=789', '/rest/product/890/ratings?sort=recent',
            # '/wp-json/wp/v2/tags?count=20', '/admin.php?tab=users&subtab=permissions',
            # f'/random{random.randint(1000,9999)}.go', f'/{uuid.uuid4()}/api/v2',
            # '/api/v3/analytics?period=week&format=json', '/rest/order/678/status?update=shipped',
            # '/wp-json/wp/v2/pages?status=draft', '/index.php?action=import&type=xml&overwrite=1',
            # f'/random{random.randint(1000,9999)}.lua', f'/{uuid.uuid4()}/endpoint/v3',
            # '/api/v1/errors?severity=critical&since=2023-01', '/rest/admin/roles?permission=edit_users',
            # '/wp-json/wp/v2/options?group=reading', '/product.php?id=890&color=blue&quantity=5',
            # f'/random{random.randint(1000,9999)}.php5', f'/{uuid.uuid4()}/resource/v2',
            # '/api/v2/restore?backup_id=123&date=2023-05-15', '/rest/system/performance?check=cpu',
            # '/wp-json/wp/v2/plugins?status=active', '/admin.php?module=reports&action=generate&type=sales',
            # '/api/v1/orders?api_key=123abc&status=pending', '/rest/user/567/profile',
            # '/wp-json/wp/v2/terms', '/index.php?controller=cart&action=add&product_id=90',
            # f'/random{random.randint(1000,9999)}.php7', f'/{uuid.uuid4()}/v1/endpoint',
            # '/api/v2/products?token=789xyz&min_price=100&max_price=500', '/rest/order/345/tracking?carrier=fedex',
            # '/wp-json/wp/v2/comments?author=15', '/admin.php?module=content&action=publish&post_id=67',
            # f'/random{random.randint(1000,9999)}.jspx', f'/{uuid.uuid4()}/resource/v3',
            # '/api/v3/users?auth_token=def456&role=moderator', '/rest/category/678/subcategories?depth=2',
            # '/wp-json/wp/v2/posts?author=5&year=2024', '/index.php?module=settings&action=save&section=general',
            # f'/random{random.randint(1000,9999)}.shtml', f'/{uuid.uuid4()}/v2/data',
            # '/api/v1/reviews?product_id=789&rating=4', '/rest/invoice/456/pay?method=credit_card',
            # '/wp-json/wp/v2/pages?parent=12&status=publish', '/page.php?section=forum&thread=popular&limit=15',
            # f'/random{random.randint(1000,9999)}.servlet', f'/{uuid.uuid4()}/service/v2',
            # '/api/v2/oauth?client_id=789&redirect_uri=https%3A%2F%2Fexample.com', '/rest/comment/567/replies?approved=1',
            # '/wp-json/wp/v2/media?mime_type=application%2Fpdf', '/admin.php?tab=security&subtab=firewall',
            # f'/random{random.randint(1000,9999)}.sh', f'/{uuid.uuid4()}/api/v3',
            # '/api/v3/reports?period=quarter&year=2023&format=csv', '/rest/cart/789/coupon?code=SAVE10',
            # '/wp-json/wp/v2/tags?slug=javascript', '/index.php?action=delete&type=user&confirm=1&id=34',
            # f'/random{random.randint(1000,9999)}.php8', f'/{uuid.uuid4()}/endpoint/v4',
            # '/api/v1/activity?user_id=45&days=30&type=login', '/rest/role/890/capabilities?add=edit_posts',
            # '/wp-json/wp/v2/settings?option=discussion', '/product.php?id=901&model=pro&warranty=2y',
            # f'/random{random.randint(1000,9999)}.ts', f'/{uuid.uuid4()}/resource/v4',
            # '/api/v2/backup?type=incremental&exclude=logs&date=2023-07-20', '/rest/system/health?check=memory',
            # '/wp-json/wp/v2/themes?status=active', '/admin.php?module=users&action=export&format=json'
        ]

        attacker_level=self.level
        # 根据攻击者水平选择URL复杂度
        if attacker_level == "初级":
            num_urls = random.randint(3, 8)       #++++++++++++++++++++++++++++++++++++++++++++++
            selected_paths = random.sample(simple_paths, num_urls)
        elif attacker_level == "中级":
            num_urls = random.randint(5, 10)
            combined_paths = simple_paths + intermediate_paths
            selected_paths = random.sample(combined_paths, num_urls)
        else:  # 高级
            num_urls = random.randint(8, 16)
            combined_paths = simple_paths + intermediate_paths + advanced_paths
            selected_paths = random.sample(combined_paths, num_urls)

        return selected_paths

    def generate_geturl_preferences(self):   #生成GET请求URL偏好
        urls = []
        if self.level == "初级":
            a=0.0
            b=0.2
        elif self.level == "中级":
            a=0.2
            b=0.3
        else:  # 高级
            a=0.8
            b=0.4

        for path in self.url_preferences:
            if random.random() < a:
                params = []
                num_params = random.randint(1, 3)
                for _ in range(num_params):
                    param_name = random.choice(['id', 'page', 'user', 'product', 'search', 'view', 'file'])
                    param_value = random.randint(1, 100) if random.random() < 0.5 else fake.word()
                    params.append(f'{param_name}={param_value}')
                if params:
                    path += '?' + '&'.join(params)
            urls.append(f"{path}")
        
        return urls

#-------Accept-Language
    def generate_accept_language_preferences(self):  #生成Accept-Language偏好+++++++++++++++++++++++++++++++++++
        # 国家与语言代码映射
        country_language_map = {
            'China': 'zh-CN',
            'United States': 'en-US',
            'Russia': 'ru-RU',
            'United Kingdom': 'en-GB',
            'Germany': 'de-DE',
            'France': 'fr-FR',
            'Japan': 'ja-JP',
            'South Korea': 'ko-KR',
            'India': 'hi-IN',
            'Canada': 'en-CA',
            'Australia': 'en-AU',
            'Brazil': 'pt-BR',
            'Mexico': 'es-MX',
            'Spain': 'es-ES',
            'Italy': 'it-IT'
        }

        # 常见语言代码
        common_languages = ['en-US', 'zh-CN', 'ru-RU', 'en-GB', 'de-DE', 'fr-FR', 'ja-JP', 'ko-KR']
        # 不常见语言代码
        uncommon_languages = ['hi-IN', 'pt-BR', 'es-MX', 'es-ES', 'it-IT', 'en-CA', 'en-AU', 'fr-CA']

        preferences = {}

        # 为高级攻击者生成自定义罕见语言代码
        if self.level == '高级':
            # 自主生成2-3个罕见语言代码
            rare_languages = []
            for _ in range(random.randint(2, 3)):    #+++++++++++++++++++++++++++++++++++
                # 生成随机前缀和后缀
                prefix = 'x-' + ''.join(random.choices(string.ascii_lowercase, k=3))
                suffix = ''.join(random.choices(string.ascii_lowercase, k=5))
                rare_languages.append(f'{prefix}-{suffix}')
        else:
            # 其他级别使用预定义的罕见语言代码
            rare_languages = ['x-pig-latin', 'x-navajo', 'x-klingon', 'x-elvish', 'qps-ploc', 'art-x-notexistent']

        if self.level == '初级':
            # 初级攻击者只使用其国家语言
            language = country_language_map.get(self.country, 'en-US')
            preferences[language] = 1.0

        elif self.level == '中级':
            # 中级攻击者主要使用其国家语言，但也会使用一些不常见的语言
            country_language = country_language_map.get(self.country, 'en-US')
            preferences[country_language] = 0.8

            # 从非国家语言中选择1-2种不常见语言
            other_languages = random.sample(uncommon_languages, random.randint(1, 2))
            for lang in other_languages:
                preferences[lang] = random.uniform(0.1, 0.2)

            # 归一化概率
            total = sum(preferences.values())
            for lang in preferences:
                preferences[lang] = preferences[lang] / total

        else:  # 高级
            # 高级攻击者使用罕见或自定义语言代码
            lang=random.choice(rare_languages)
            preferences[lang] = 0.9

            num_languages = random.randint(2, 3)
            selected_languages = random.sample(common_languages+uncommon_languages, num_languages)

            for lang in selected_languages:
                preferences[lang] = random.uniform(0.05, 0.1)

            # 归一化概率
            total = sum(preferences.values())
            for lang in preferences:
                preferences[lang] = preferences[lang] / total
        # print(preferences)
        return preferences

    def generate_accept_language(self):  #根据Accept-Language偏好生成Accept-Language值
        # 根据偏好生成Accept-Language值
        languages = list(self.accept_language_preferences.keys())
        probabilities = list(self.accept_language_preferences.values())
        return np.random.choice(languages, p=probabilities)

#-------X-Forwarded-For
    def generate_x_forwarded_for_preference(self):  #生成X-Forwarded-For偏好+++++++++++++++++
        xff=[]
        # 根据攻击者水平生成X-Forwarded-For
        if self.level == "中级":
            for _ in range(random.randint(1,3)):   #++++++++++++++++++++++++++++++++
                # 伪造少量IP池或内网IP
                if random.random() < 0.5:
                    # 内网IP
                    private_ips = [
                        f'10.{random.randint(0,255)}.{random.randint(0,255)}.{random.randint(1,254)}',
                        f'192.168.{random.randint(0,255)}.{random.randint(1,254)}',
                        f'172.{random.randint(16,31)}.{random.randint(0,255)}.{random.randint(1,254)}'
                    ]
                    xff.append(random.choice(private_ips))
                else:
                    # 伪造少量IP池
                    fake_ips = [fake.ipv4() for _ in range(10)]
                    xff.append(random.choice(fake_ips))

        else:  # 高级
            for _ in range(random.randint(2,5)):   #++++++++++++++++++++++++++++++++++++
                # 动态伪造大量IP或代理链
                if random.random() < 0.2:
                    xff.append(' ')  # 空值
                elif random.random() < 0.2:
                    xff.append('unknown')
                elif random.random() < 0.3:
                    # 代理链格式
                    xff.append(f'{fake.ipv4()}, {fake.ipv4()}, {fake.ipv4()}')
                else:
                    # 随机IP
                    xff.append(fake.ipv4())
        # print(xff)
        return xff

    def get_x_forwarded_for(self, real_ip):  #根据X-Forwarded-For偏好获取X-Forwarded-For值
        if self.level == "初级":
            # 不伪造IP
            return real_ip
        elif self.level == "中级":
            if random.random() < 0.3:
                return real_ip
            else:
                xff=self.x_forwarded_for_preferences
                return random.choice(xff)
        elif self.level == "高级":
            xff=self.x_forwarded_for_preferences
            return random.choice(xff)

#-------payload
    def generate_payload_preferences(self):   #目前高级攻击者载荷没有做复杂变化+++++++++++++++++++++++++++++++++++++
        # 初始化载荷池
        payload_pool = {}
        csv_path = os.path.join(os.path.dirname(__file__), 'payloads.csv')
        
        with open(csv_path, 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                attack_type = row['attack_type']
                level = row['level']
                payload = row['payload']
                
                # 按攻击类型和级别组织载荷
                if attack_type not in payload_pool:
                    payload_pool[attack_type] = {}
                if level not in payload_pool[attack_type]:
                    payload_pool[attack_type][level] = []
                payload_pool[attack_type][level].append(payload)
        
        # 根据攻击者级别选择合适的载荷池
        attacker_level = self.level
        selected_payloads = {}
        
        for attack_type, levels_payloads in payload_pool.items():
            # 为每种攻击类型选择合适的载荷
            available_payloads = []
            
            if attacker_level == "初级":
                # 初级攻击者只能使用low级别的载荷
                if 'low' in levels_payloads:
                    available_payloads = levels_payloads['low']
                # 随机选择2-3个载荷
                num_payloads = random.randint(1, 2)   #++++++++++++++++++++++++++++++++++++++++++++++
            elif attacker_level == "中级":
                # 中级攻击者可以使用low和medium级别的载荷
                if 'medium' in levels_payloads:
                    available_payloads.extend(levels_payloads['medium'])
                if 'low' in levels_payloads:
                    available_payloads.extend(levels_payloads['low'])
                # 随机选择3-4个载荷
                num_payloads = random.randint(3, 5)   #++++++++++++++++++++++++++++++++++++++++++++++
            else:  # high
                # 高级攻击者可以使用所有级别的载荷，但偏向于使用high级别的载荷
                if 'high' in levels_payloads:
                    # available_payloads.extend(levels_payloads['high'])
                    available_payloads.extend(levels_payloads['medium'])
                    available_payloads.extend(levels_payloads['low'])
                else:
                    if 'medium' in levels_payloads:
                        available_payloads.extend(levels_payloads['medium'])
                    if 'low' in levels_payloads:
                        available_payloads.extend(levels_payloads['low'])
                # 随机选择4-5个载荷
                num_payloads = random.randint(5, 8)   #++++++++++++++++++++++++++++++++++++++++++++++
            
            # 确保不重复选择载荷
            if available_payloads:
                # 如果可用载荷数量少于需要的数量，则全部选择
                if len(available_payloads) <= num_payloads:
                    selected_payloads[attack_type] = available_payloads
                else:
                    # 随机选择指定数量的载荷
                    selected_payloads[attack_type] = random.sample(available_payloads, num_payloads)
        # print(selected_payloads)
        return selected_payloads

    def generate_payload(self, phase,attack_type, location):
        # 根据攻击者水平调整载荷复杂度
        # if self.level == "高级" and random.random() < 0.7:
        #     # 高级攻击者可能使用更复杂的载荷
        #     if attack_type == "SQLInjection":
        #         # 添加更多SQL注入技巧
        #         techniques = [
        #             lambda p: f'({p})',
        #             lambda p: p.replace('--', '#'),
        #             lambda p: p.replace(' ', '/**/'),
        #             lambda p: p + ' LIMIT 1'
        #         ]
        #         for _ in range(random.randint(1, 3)):
        #             base_payload = random.choice(techniques)(base_payload)

        base_payload = random.choice(self.payload_preferences[attack_type])
        # 编码载荷
        encoded_payload, url_encoded, unicode_encoded, base64_encoded, html_comment =self.get_encode_payload(phase,base_payload, location)
        return encoded_payload, base_payload, url_encoded, unicode_encoded, base64_encoded, html_comment

    def generate_encoding_preferences(self):
        # 生成攻击者偏好的编码方式
        encodings = {"url": 0, "unicode": 0, "base64": 0, "html_comment": 0}

        if self.level == "初级":
            encodings["url"] = 0
        elif self.level == "中级":
            encodings["url"] = random.uniform(0.4, 0.9)
            encodings["unicode"] = 0
            encodings["base64"] = random.uniform(0.2, 0.6)
            encodings["html_comment"] = 0
        else:  # 高级
            encodings["url"] = random.uniform(0.5, 0.9)
            encodings["unicode"] = random.uniform(0.5, 0.9)
            encodings["base64"] = random.uniform(0.3, 0.8)
            encodings["html_comment"] = random.uniform(0.2, 0.7)

        return encodings

    def get_encode_payload(self, phase, payload, location):

        url_encoded = 0
        unicode_encoded = 0
        base64_encoded = 0
        html_comment = 0

        encoded_payload = payload

        if  self.level=="初级" and phase=="reconnaissance": #初级侦察阶段不编码
            return encoded_payload, url_encoded, unicode_encoded, base64_encoded, html_comment
        if  self.level=="中级" and phase=="reconnaissance": #中级侦察阶段不编码
            return encoded_payload, url_encoded, unicode_encoded, base64_encoded, html_comment


        # 根据攻击者水平和位置决定编码方式
        if self.level == "初级":
            # 初级攻击者：很少编码，即使编码也只是基础URL编码
            if location == "URL" and random.random() < self.encoding_preferences["url"]:
                encoded_payload = quote(payload)
                url_encoded = 1

        elif self.level == "中级":
            # 中级攻击者：能使用基础编码
            if location == "URL" and random.random() < self.encoding_preferences["url"]:
                encoded_payload = quote(payload)
                url_encoded = 1
                if random.random() < self.encoding_preferences["url"]:
                    # 偶尔双重URL编码
                    encoded_payload = quote(encoded_payload)
                    url_encoded = 2

            elif location in ["Cookie", "Post Body"] and random.random() < self.encoding_preferences["base64"]:
                # Cookie或Post Body中可能使用Base64编码
                encoded_payload = base64.b64encode(payload.encode()).decode()
                base64_encoded = 1

            if random.random() < self.encoding_preferences["unicode"]:
                # Unicode编码
                encoded_payload = ''.join(f'\\u{ord(c):04x}' for c in encoded_payload)
                unicode_encoded = 1

            if random.random() < self.encoding_preferences["html_comment"]:
                # HTML注释绕过
                encoded_payload = f'<!--{encoded_payload}-->'
                html_comment = 1
        else:  # 高级攻击者
            # 高级攻击者：多种复杂编码组合
            encoding_steps = []
            if random.random() < self.encoding_preferences["url"]:
                encoding_steps.append("url")
            if random.random() < self.encoding_preferences["unicode"]:
                encoding_steps.append("unicode")
            if random.random() < self.encoding_preferences["base64"]:
                encoding_steps.append("base64")
            if random.random() < self.encoding_preferences["html_comment"]:
                encoding_steps.append("html_comment")

            for encoding in encoding_steps:
                if encoding == "url":
                    # 多重URL编码
                    for _ in range(random.randint(1, 3)):
                        encoded_payload = quote(encoded_payload)
                        url_encoded += 1
                elif encoding == "unicode":
                    encoded_payload = ''.join(f'\\u{ord(c):04x}' for c in encoded_payload)
                    unicode_encoded = 1
                elif encoding == "base64":
                    encoded_payload = base64.b64encode(encoded_payload.encode()).decode()
                    base64_encoded = 1
                elif encoding == "html_comment":
                    # 复杂HTML注释绕过
                    encoded_payload = f'<!--{encoded_payload[:len(encoded_payload)//2]}-->{encoded_payload[len(encoded_payload)//2:]}'
                    html_comment = 1

        return encoded_payload, url_encoded, unicode_encoded, base64_encoded, html_comment

#-------post_body
    def generate_post_body_preference(self):   #++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        if self.level=="初级":
            a=random.randint(1,3)       #++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        elif self.level=="中级":
            a=random.randint(3,6)
        else:
            a=random.randint(5,8)
        
        post_body_preference=[]

        for _ in range(a):
            if random.random() < 0.5:
                # 表单数据
                fields = []
                num_fields = random.randint(2, 5)
                for _ in range(num_fields):
                    field_name = random.choice(['username', 'password', 'email', 'name', 'address', 'phone', 'submit'])
                    if field_name == 'username':
                        field_value = fake.user_name()
                    elif field_name == 'password':
                        field_value = fake.password()
                    elif field_name == 'email':
                        field_value = fake.email()
                    elif field_name == 'name':
                        field_value = fake.name()
                    elif field_name == 'address':
                        field_value = fake.address().replace('\n', ', ')
                    elif field_name == 'phone':
                        field_value = fake.phone_number()
                    else:
                        field_value = 'Submit'
                    fields.append(f'{field_name}={field_value}')
                post_body_preference.append('&'.join(fields))

            else:
                # JSON数据
                data = {}
                num_fields = random.randint(2, 5)
                for _ in range(num_fields):
                    field_name = random.choice(['user', 'pass', 'email', 'name', 'address', 'phone', 'action'])
                    if field_name == 'user':
                        data[field_name] = fake.user_name()
                    elif field_name == 'pass':
                        data[field_name] = fake.password()
                    elif field_name == 'email':
                        data[field_name] = fake.email()
                    elif field_name == 'name':
                        data[field_name] = fake.name()
                    elif field_name == 'address':
                        data[field_name] = fake.address().replace('\n', ', ')
                    elif field_name == 'phone':
                        data[field_name] = fake.phone_number()
                    else:
                        data[field_name] = random.choice(['login', 'register', 'submit'])
                post_body_preference.append(json.dumps(data))
        # print(self.attacker_id,':',post_body_preference,'/n')
        return post_body_preference

    def generate_post_body(self, http_method, payload_location):
        # 只有POST, PUT, PATCH有Post Body
        if http_method not in ["POST", "PUT", "PATCH"]:
            return ""

        # 如果载荷在Post Body中，返回载荷
        if payload_location == "Post Body":
            # 这里会在主程序中填充实际载荷
            return "{placeholder}"

        return random.choice(self.post_body_preference)

#----------------------
    def generate_attack_frequency(self):
        # 生成攻击频率
        if self.level == "初级":
            return {"type": "burst", "avg_interval": random.randint(5, 120)}  # 平均间隔5-60秒

        elif self.level == "中级":
            return {"type": "regular", "avg_interval": random.randint(60, 800)}  # 平均间隔60-300秒

        else:  # 高级
            return {"type": "low", "avg_interval": random.randint(500, 2000)}

    def generate_time_preference(self): # 生成时间偏好，喜好的小时数
        # 生成时间偏好
        if self.level == "初级":
            # 个人时间（晚上）
            hours = [18, 19, 20, 21, 22, 23, 0, 1,2]  # 总共 8 个小时
            # 随机选择时间段长度
            num_hours = random.randint(1, 3)
            # 找出所有可以作为起始点的索引（不会越界）
            start_indices = [i for i in range(len(hours)) if (i + num_hours <= len(hours))]
            # 随机选一个起始点
            start_idx = random.choice(start_indices)
            # 取出连续时间段
            selected_hours = hours[start_idx : start_idx + num_hours]
            # print(self.attacker_id,'初级:',selected_hours)
            return {"type": "personal", "hours": selected_hours}
        elif self.level == "中级":
            # 工作时间（白天）
            hours = [8,9, 10, 11, 12, 13, 14, 15, 16, 17]  # 总共 9 个小时
            num_hours = random.randint(3, 6)
            start_indices = [i for i in range(len(hours)) if (i + num_hours <= len(hours))]
            start_idx = random.choice(start_indices)
            selected_hours = hours[start_idx : start_idx + num_hours]
            # print(self.attacker_id,'中级:',selected_hours)
            return {"type": "work", "hours": selected_hours}
        else:  # 高级
            # 无明显规律
            hours = list(range(0, 24))  # 总共 24 个小时
            num_hours = random.randint(6, 10)
            start_indices = [i for i in range(len(hours)) if (i + num_hours <= len(hours))]
            start_idx = random.choice(start_indices)
            selected_hours = hours[start_idx : start_idx + num_hours]
            # print(self.attacker_id,'高级:',selected_hours)
            return {"type": "random", "hours": selected_hours}

    def generate_attack_time(self, start_date, end_date): # 生成攻击开始时间
        # 生成符合时间偏好的攻击时间
        time_diff = end_date - start_date # 计算时间差
        random_days = random.randint(0, time_diff.days) # 随机选择天数
        attack_date = start_date + datetime.timedelta(days=random_days) # 随机选择一天

        # 根据时间偏好选择小时
        if self.time_preference["type"] == "personal":
            # 个人时间偏好：晚上或周末
            # if attack_date.weekday() >= 5:  # 周末
            #     local_hour = random.choice(range(0, 24))
            # else:  # 工作日
                local_hour = random.choice(self.time_preference["hours"])
        elif self.time_preference["type"] == "work":
            # 工作时间偏好：白天
            # while attack_date.weekday() >= 5:  
            #     random_days = random.randint(0, time_diff.days) 
            #     attack_date = start_date + datetime.timedelta(days=random_days) 
            local_hour = random.choice(self.time_preference["hours"])
            # else:  # 周末
            #     # 周末攻击概率降低
            #     if random.random() < 0.8:
            #         local_hour = random.choice(self.time_preference["hours"])
            #     else:
            #         local_hour = random.choice(range(0, 24))
        else:  # random
            local_hour = random.choice(self.time_preference["hours"])

        # 转换为UTC小时
        # 处理时区偏移中的小数部分，转换为整数小时
        utc_hour = int(round((local_hour - self.timezone_offset) % 24))

        minute = random.randint(0, 59)
        second = random.randint(0, 59)

        attack_time = datetime.datetime(
            attack_date.year, attack_date.month, attack_date.day,
            utc_hour, minute, second,
            tzinfo=pytz.utc
        )

        return attack_time

    def generate_phase_preference(self, basenum):
        phases = []
        current_phase = "reconnaissance"
        if self.level == "初级":
            if self.noise_flag!=True:
                reconnaissance_count = random.randint(round(basenum * 1), round(basenum * 5))
                exploitation_count   = random.randint(round(basenum * 0.5), round(basenum * 1.5))
                persistence_count    = random.randint(round(basenum * 0.0), round(basenum * 1.0))      
     
            else:    #噪声
                reconnaissance_count = random.randint(round(basenum * 3), round(basenum * 7))
                if random.random() < 0.5:
                    exploitation_count   = 0
                    persistence_count    = 0 
                else:
                    exploitation_count   = random.randint(0, round(basenum * 1))
                    if random.random() < 0.5:
                        persistence_count    = 0                     
                    else:
                        persistence_count    = random.randint(0, round(basenum * 1))                     

            # s_length = random.randint(basenum, round(basenum * 1.5))+random.randint(basenum, round(basenum * 1.5))+random.randint(0, round(basenum * 0.5))
            
            # a=random.uniform(0.05,0.1)
            # b=random.uniform(0.05,0.1)

            # for _ in range(s_length):
            #     phases.append(current_phase)
            #     # 有概率转换阶段
            #     if current_phase == "reconnaissance" and random.random() < a:
            #         current_phase = "exploitation"
            #     elif current_phase == "exploitation" and random.random() < b:
            #         current_phase = "persistence"

            
        elif self.level == "中级" :  # 中级攻击者
            if self.noise_flag!=True:   #非噪声
                reconnaissance_count = random.randint(round(basenum * 0.5), round(basenum * 3))
                exploitation_count   = random.randint(round(basenum * 0.5), round(basenum * 2))
                if self.attacker_id!=6:
                    persistence_count    = random.randint(round(basenum * 0.0), round(basenum * 2.0))      
                else:
                    persistence_count    = 0  
            else:   #噪声无攻击维持阶段，适当延长侦察阶段
                reconnaissance_count = random.randint(round(basenum * 3), round(basenum * 7))
                if random.random() < 0.5:
                    exploitation_count   = 0
                    persistence_count    = 0 
                else:
                    exploitation_count   = random.randint(0, round(basenum * 3))
                    if random.random() < 0.5:
                        persistence_count    = 0 
                    else:
                        persistence_count    = random.randint(0, round(basenum * 1))                     

            # s_length = random.randint(basenum, round(basenum * 1.5))+random.randint(basenum, round(basenum * 1.5))+random.randint(0, round(basenum * 0.5))
            # a=random.uniform(0.1,0.2)
            # b=random.uniform(0.2,0.25)
            # # c=random.uniform(0.05,0.15)

            # for _ in range(s_length):
            #     phases.append(current_phase)
            #     # 有概率转换阶段
            #     if current_phase == "reconnaissance" and random.random() < a:
            #         current_phase = "exploitation"
            #     elif current_phase == "exploitation" and random.random() < b:
            #         current_phase = "persistence"
            #     # elif current_phase == "persistence" and random.random() < c:
            #     #     current_phase = "reconnaissance"

        else: # 高级攻击者
            if self.noise_flag!=True:
                reconnaissance_count = random.randint(round(basenum * 0.1), round(basenum * 2))
                exploitation_count   = random.randint(round(basenum * 0.1), round(basenum * 3))
                persistence_count    = random.randint(round(basenum * 0.5), round(basenum * 3)) 
            else:   #噪声无攻击维持阶段，适当延长侦察阶段
                reconnaissance_count = random.randint(round(basenum * 3), round(basenum * 7))
                if random.random() < 0.5:
                    exploitation_count   = 0
                    persistence_count    = 0 
                else:
                    exploitation_count   = random.randint(round(basenum * 0.1), round(basenum * 2))
                    if random.random() < 0.5:
                        persistence_count    = 0 
                    else:
                        persistence_count    = random.randint(round(basenum * 0.5), round(basenum * 3))                     

            # s_length = random.randint(basenum, round(basenum * 1.5))+random.randint(basenum, round(basenum * 1.5))+random.randint(0, round(basenum * 0.5))
            # a=random.uniform(0.15,0.25)
            # b=random.uniform(0.25,0.35)

            # for _ in range(s_length):
            #     phases.append(current_phase)
            #     # 有概率转换阶段
            #     if current_phase == "reconnaissance" and random.random() < a:
            #         current_phase = "exploitation"
            #     elif current_phase == "exploitation" and random.random() < b:
            #         current_phase = "persistence"
        phases = (["reconnaissance"] * reconnaissance_count + ["exploitation"] * exploitation_count + ["persistence"] * persistence_count)
     
        return phases


##############################################################################################################################################

class AttackDatasetGenerator:
    def __init__(self, num_attackers,weights,basenum):
        self.num_attackers = num_attackers
        self.weights = weights
        self.basenum=basenum
        self.sequence_length = 0 # 攻击序列长度
        self.attacker_profiles = [] # 攻击者档案
        self.used_ip_prefixes = [] # IP前缀跟踪集合
        self.attackers = self.generate_attackers()
        self.dataset = []
        self.noise_dataset=[]
        self.last_attack_times = {} # 每个攻击者上次攻击时间

    def generate_attackers(self):#生成攻击者
        attackers = []
        attacker_levels_cache=[]  # 攻击者水平缓存

        a=round(self.num_attackers*self.weights[0])
        b=round(self.num_attackers*self.weights[1])
        c=round(self.num_attackers*self.weights[2])


        for _ in range(a):
            attacker_levels_cache.append("初级")
        for _ in range(b):
            attacker_levels_cache.append("中级")
        for _ in range(c):
            attacker_levels_cache.append("高级")

        aa=15
        bb=16
        cc=10
        for _ in range(a):     #第二次添加的为噪声
            for _ in range(aa):
                attacker_levels_cache.append("初级")
        for _ in range(b):
            for _ in range(bb):
                attacker_levels_cache.append("中级")
        for _ in range(c):
            for _ in range(cc):
                attacker_levels_cache.append("高级")
        # print(a,b,c,aa,bb,cc)

        # len(attacker_levels_cache)
        noise_flag=None
        for i in range(1, len(attacker_levels_cache) + 1):
            level = attacker_levels_cache[i-1]
            country = random.choice(list(country_ip_ranges.keys()))
            if i<=self.num_attackers:
                noise_flag=False  
                attacker = Attacker(i, level, country, self.used_ip_prefixes,self.basenum,noise_flag)   
            else:
                noise_flag=True   # 噪声攻击者
                attacker = Attacker(-1, level, country, self.used_ip_prefixes,self.basenum,noise_flag)   



            self.used_ip_prefixes=attacker.used_ip_prefixes
            attackers.append(attacker)

            # 记录攻击者档案
            self.attacker_profiles.append({
                "攻击者ID": i,
                "水平": level,
                "国家": country,
                "偏好HTTP方法": attacker.http_method_preferences,
                "偏好攻击类型": attacker.attack_type_preferences,
                "偏好载荷位置": attacker.payload_location_preferences,
                "攻击频率": attacker.attack_frequency,
                "时间偏好": attacker.time_preference["type"]
            })

        true_num=self.num_attackers
        num=[(true_num,0),(true_num+1,1),(true_num+2,2),(true_num+a*aa,3),(true_num+a*aa+1,4),(true_num+a*aa+2,5),(true_num+a*aa+3,6),(true_num+a*aa+4,7),(true_num+a*aa+b*bb,8),(true_num+a*aa+b*bb+1,9)]

        


        for i,j in num:
            # attackers[i].country = attackers[j].country
            # attackers[i].timezone_offset = attackers[j].timezone_offset 
            attackers[i].host_preferences = attackers[j].host_preferences 
             
            attackers[i].encoding_preferences = attackers[j].encoding_preferences 
            # attackers[i].attack_frequency = attackers[j].attack_frequency 
            attackers[i].http_method_preferences = attackers[j].http_method_preferences 
            attackers[i].payload_preferences = attackers[j].payload_preferences 
            attackers[i].url_preferences = attackers[j].url_preferences 
            attackers[i].geturl_preferences = attackers[j].geturl_preferences 
            attackers[i].accept_language_preferences = attackers[j].accept_language_preferences 
            # attackers[i].x_forwarded_for_preferences = attackers[j].x_forwarded_for_preferences 
            # attackers[i].post_body_preference = attackers[j].post_body_preference

            attackers[i].phase_preference = attackers[j].phase_preference
            attackers[i].attack_type_preferences = attackers[j].attack_type_preferences
            attackers[i].reconnaissance_catalog =attackers[j].reconnaissance_catalog
            attackers[i].reconnaissance_catalog = attackers[i].generate_reconnaissance_catalog(attackers[i].reconnaissance_catalog)
            attackers[i].exploitation_catalog = attackers[j].exploitation_catalog
            attackers[i].exploitation_catalog = attackers[i].generate_exploitation_catalog(attackers[i].exploitation_catalog)
            attackers[i].payload_location_preferences = attackers[j].payload_location_preferences
            attackers[i].noise_save=True


        # print(attackers[31].attack_type_preferences[:3])
        # print(attackers[31].reconnaissance_catalog)
        # # attackers[31].reconnaissance_catalog = attackers[31].generate_reconnaissance_catalog(attackers[31].reconnaissance_catalog)
        # print(attackers[31].attack_type_preferences[:3])
        # print(attackers[31].reconnaissance_catalog)
        # # print(attackers[3].attack_type_preferences[:3])
        # # print(attackers[3].reconnaissance_catalog)

        # print(attackers[31].attack_type_preferences[3:9])
        # print(attackers[31].exploitation_catalog)
        # # attackers[31].exploitation_catalog = attackers[31].generate_exploitation_catalog(attackers[31].exploitation_catalog)
        # print(attackers[31].attack_type_preferences[3:9])
        # print(attackers[31].exploitation_catalog)







        return attackers

    def generate_dataset(self,basenum):

        # 生成数据集
        print(f"开始生成数据集，共{self.num_attackers}个攻击者...")

        # 设置时间范围（过去7天）
        end_date = datetime.datetime.now(pytz.utc)  # 结束时间（UTC）
        start_date = end_date - datetime.timedelta(days=7)  # 开始时间（UTC）

        # 为每个攻击者生成攻击

        for i, attacker in enumerate(self.attackers):
            # print(i,'-----------')  从0开始
             
            # 生成起始时间
            start_time = attacker.generate_attack_time(start_date, end_date) # 生成起始时间

            self.sequence_length=0
            
            ip_nums=len(attacker.ip_addresses)
            # print('ip_nums:',ip_nums)

            ip_count=0
            for j in attacker.ip_addresses: 
                ip_count+=1
                self.last_attack_times[i]=0  # 更换ip后，上次攻击时间重置为0

                # host_preferences1=host_preferences2=host_preferences3=attacker.host_preferences
                # url_preferences1=url_preferences2=url_preferences3=attacker.url_preferences
                # post_body_preference1=post_body_preference2=post_body_preference3=attacker.post_body_preference

                if attacker.attacker_id==4 and (ip_count%2==0   ):    #Y型簇  (math.ceil(ip_nums/2)+1)
                    # print(ip_count,'+++++Change!!!!!!!!!!!!!')
                    # attacker.attack_type_preferences=attacker.generate_attack_type_preferences()    #++++++
                    # attacker.payload_location_preferences = attacker.generate_payload_location_preferences()
                    # attacker.encoding_preferences = attacker.generate_encoding_preferences()
                    # attacker.http_method_preferences = attacker.generate_http_method_preferences()
                    # attacker.payload_preferences = attacker.generate_payload_preferences()     #+++++

                    
                    attacker.host_preferences = random.sample(attacker.host_preferences,max(len(attacker.host_preferences)-1,1))
                    temp_host=random.sample(attacker.generate_host_preferences(),1)
                    # print(temp_host)
                    attacker.host_preferences+=temp_host
                    # print('++++++++++++++++',attacker.host_preferences)
      

                    attacker.url_preferences = random.sample(attacker.url_preferences,max(len(attacker.url_preferences)-1,1))
                    temp_host=random.sample(attacker.generate_url_preferences(),1)
                    attacker.url_preferences.append(temp_host[0])
                    # print(attacker.url_preferences)
                  

                    attacker.post_body_preference = random.sample(attacker.post_body_preference,max(len(attacker.post_body_preference)-1,1))
                    temp_host=random.sample(attacker.generate_post_body_preference(),1)
                    attacker.post_body_preference.append(temp_host[0])
                    # print(attacker.post_body_preference)
                    

                    # elif ip_count%3==2:
                    #     attacker.host_preferences = random.sample(host_preferences2,len(host_preferences2)-1)
                    #     temp_host=random.sample(attacker.generate_host_preferences(),1)
                    #     attacker.host_preferences+=temp_host
                    #     host_preferences2=attacker.host_preferences

                    #     attacker.url_preferences = random.sample(url_preferences2,len(url_preferences2)-1)
                    #     temp_host=random.sample(attacker.generate_url_preferences(),1)
                    #     attacker.url_preferences.append(temp_host[0])
                    #     url_preferences2=attacker.url_preferences

                    #     attacker.post_body_preference = random.sample(post_body_preference2,len(post_body_preference2)-1)
                    #     temp_host=random.sample(attacker.generate_post_body_preference(),1)
                    #     attacker.post_body_preference.append(temp_host[0])
                    #     post_body_preference2=attacker.post_body_preference

                    # elif ip_count%3==0:
                    #     attacker.host_preferences = random.sample(host_preferences3,len(host_preferences3)-1)
                    #     temp_host=random.sample(attacker.generate_host_preferences(),1)
                    #     attacker.host_preferences+=temp_host
                    #     host_preferences3=attacker.host_preferences

                    #     attacker.url_preferences = random.sample(url_preferences3,len(url_preferences3)-1)
                    #     temp_host=random.sample(attacker.generate_url_preferences(),1)
                    #     attacker.url_preferences.append(temp_host[0])
                    #     url_preferences3=attacker.url_preferences

                    #     attacker.post_body_preference = random.sample(post_body_preference3,len(post_body_preference3)-1)
                    #     temp_host=random.sample(attacker.generate_post_body_preference(),1)
                    #     attacker.post_body_preference.append(temp_host[0])
                    #     post_body_preference3=attacker.post_body_preference
                    # attacker.host_preferences = random.sample(attacker.generate_host_preferences(),1)
                    # attacker.url_preferences = random.sample(attacker.generate_host_preferences(),1)
                    # attacker.x_forwarded_for_preferences = attacker.generate_x_forwarded_for_preference()
                    # attacker.post_body_preference=attacker.generate_post_body_preference()


                

                # 生成每个ip地址的攻击序列
                attacks = self.generate_attack_sequence(attacker, start_time,basenum,linshi_id=i,ip_address=j)  # 每个ip生成一个攻击序列

                # 添加到数据集
                if attacker.noise_flag and attacker.noise_save==False:
                    self.noise_dataset.extend(attacks)
                else:
                    self.dataset.extend(attacks)
                

            print(f"{i}生成攻击者{attacker.attacker_id}（{attacker.level}）的{self.sequence_length}个攻击记录...")

        # 按时间排序
        self.dataset.sort(key=lambda x: x["攻击警报时间"])

        # 更新序号
        for i, attack in enumerate(self.dataset):
            attack["序号"] = i + 1

        ip_sum=0
        ip_noise_sum=0

        for i,attacker in enumerate(self.attackers):
            if attacker.noise_flag:
                # print(attacker.ip_addresses)
                ip_noise_sum+=len(attacker.ip_addresses)
            ip_sum+=len(attacker.ip_addresses)

         
        print("数据集生成完成！ip地址总数量为:",ip_sum,"   噪声ip地址数量为:",ip_noise_sum)

    def generate_attack_sequence(self, attacker, start_time,basenum,linshi_id,ip_address):#生成攻击序列，在此确定了攻击序列的长度和攻击者攻击阶段的偏好

        # 生成攻击序列
        attacks = []
        current_time = start_time
        
        # 调用generate_phase_preference函数获取基础的阶段偏好
        phases = attacker.phase_preference

        # 存储连续出现的结果，每个元素是(单词, 连续次数)
        consecutive_counts = []
        current_phase = phases[0]
        current_count = 1
        
        # 从第二个元素开始遍历
        for phase in phases[1:]:
            if phase == current_phase:
                current_count += 1
            else:
                # 遇到不同的单词，记录前一个单词的连续次数
                consecutive_counts.append((current_phase, current_count))
                current_phase = phase
                current_count = 1
        
        # 记录最后一组连续元素
        consecutive_counts.append((current_phase, current_count))

        # 定义随机修改函数
        def apply_random_modification(count, fluctuation_percent=0.1):
            if count == 0:
                return 0
            # 计算最大可修改数量
            max_change = max(1, int(count * fluctuation_percent))
            # 随机增加或减少数量
            change = random.randint(-max_change, max_change)
            # 确保结果不为负数
            return max(1, count + change) if count > 0 else 0
        
        # 根据攻击者水平确定随机波动范围
        if attacker.level == "初级":
            # 初级攻击者：波动范围较小（±5%）
            fluctuation_range = 0.1
        elif attacker.level == "中级":
            # 中级攻击者：波动范围中等（±15%）
            fluctuation_range = 0.15
        else:  # 高级
            # 高级攻击者：波动范围较大（±20%）
            fluctuation_range = 0.2
        
        # 对每个阶段的数量进行随机修改
        modified_counts=[]
        for word, count in consecutive_counts:#对每个阶段的数量进行随机修改
            change = apply_random_modification(count, fluctuation_range)
            modified_counts.append((word, change))
        
        # 更新sequence_length
        self.sequence_length += sum([item[1] for item in modified_counts])

        # 生成攻击序列
        # origin_attack_frequency=attacker.attack_frequency
        origin_host_preferences=attacker.host_preferences
        origin_x_forwarded_for_preferences=attacker.x_forwarded_for_preferences
        origin_url_preferences=attacker.url_preferences
        origin_post_body_preference=attacker.post_body_preference

        for phase,phase_count in modified_counts:  #
            # print(phase,phase_count)
            for i in range(phase_count):
                if linshi_id==5:
                    if phase=='reconnaissance':
                        attacker.host_preferences = origin_host_preferences
                        attacker.x_forwarded_for_preferences = origin_x_forwarded_for_preferences
                        attacker.url_preferences = origin_url_preferences
                        attacker.post_body_preference = origin_post_body_preference
                    if phase=='exploitation' :   #reconnaissance
                        # print('++++++++++++++++',attacker.host_preferences,origin_host_preferences)
                        attacker.host_preferences = origin_host_preferences[:math.ceil(len(origin_host_preferences)/2)]
                        # print('---------------',attacker.host_preferences,origin_host_preferences)
                        attacker.x_forwarded_for_preferences = origin_x_forwarded_for_preferences[:math.ceil(len(origin_x_forwarded_for_preferences)/2)]
                        attacker.url_preferences = origin_url_preferences[:math.ceil(len(origin_url_preferences)/2)]
                        attacker.post_body_preference = origin_post_body_preference[:math.ceil(len(origin_post_body_preference)/2)]
                    if phase=='persistence':
                        attacker.host_preferences = origin_host_preferences[:math.ceil(len(origin_host_preferences)/4)]
                        attacker.x_forwarded_for_preferences = origin_x_forwarded_for_preferences[:math.ceil(len(origin_x_forwarded_for_preferences)/4)]
                        attacker.url_preferences = origin_url_preferences[:math.ceil(len(origin_url_preferences)/4)]
                        attacker.post_body_preference = origin_post_body_preference[:math.ceil(len(origin_post_body_preference)/4)]

                # 根据攻击频率生成时间间隔
                if attacker.attack_frequency["type"] == "burst":  #初级，时间间隔短，正态分布
                    interval = int(round(random.gauss(attacker.attack_frequency["avg_interval"], attacker.attack_frequency["avg_interval"] // 10)))
                    if interval >60:
                        interval=60
                    elif interval<1:
                        interval=1
                elif attacker.attack_frequency["type"] == "regular":  #中级，时间间隔中等，正态分布
                    interval = int(round(random.gauss(attacker.attack_frequency["avg_interval"], attacker.attack_frequency["avg_interval"] // 15)))
                else: 
                    interval = int(round(random.gauss(attacker.attack_frequency["avg_interval"], attacker.attack_frequency["avg_interval"] // 30)))

                current_time += datetime.timedelta(seconds=interval)
                current_local_hour=int(round((current_time.hour + attacker.timezone_offset) % 24))  #计算当前时间的本地时间
                if current_local_hour not in attacker.time_preference["hours"]:  #如果当前时间的本地时间不在时间偏好的范围内，就将时间重置为时间偏好的第一个小时
                    new_utc_hour=int(round((attacker.time_preference["hours"][0] - attacker.timezone_offset) % 24))
                    if new_utc_hour == 24:
                        new_utc_hour = 0
                    try:
                        current_time = current_time.replace(hour=new_utc_hour)
                    except ValueError as e:
                        print(f"Error: {e}")
                        print(f"new_utc_hour: {new_utc_hour}")
                    current_time += datetime.timedelta(days=1)  #如果超出了时间偏好的范围，就将时间重置为时间偏好的第一个小时，并且日期加1

                attack = self.generate_single_attack(attacker, current_time, phase,phase_count,i,ip_address,linshi_id)
                attacker.host_preferences = origin_host_preferences
                attacker.x_forwarded_for_preferences = origin_x_forwarded_for_preferences
                attacker.url_preferences = origin_url_preferences
                attacker.post_body_preference = origin_post_body_preference

                attacks.append(attack)

        return attacks

    def generate_single_attack(self, attacker, attack_time, phase,phase_count,index,ip_address,linshi_id):#生成单个攻击
        attacker_id = attacker.attacker_id
        attacker_level = attacker.level
        attacker_country = attacker.country

        # 计算与上次攻击的时间间隔
        last_time = self.last_attack_times.get(linshi_id)

        if last_time:
            time_diff = (attack_time - last_time).total_seconds()
        else:
            time_diff = 0  # 第一次攻击，时间间隔为0
        self.last_attack_times[linshi_id] = attack_time
        
        # 攻击源IP
        # src_ip = random.choice(attacker.ip_addresses)
        src_ip=ip_address

        #源ip所在国家
        def find_country(ip): #根据IP地址查找国家
            first_octet = ip.split('.')[0]
            # 构建简化的网段标识（如"113.0.0.0/8" -> "113"）
            target = f"{first_octet}."
            
            # 遍历所有国家的IP段进行匹配
            for country, cidrs in country_ip_ranges.items():
                for cidr in cidrs:
                    # CIDR格式为"xxx.0.0.0/8"，提取前8位标识
                    if cidr.startswith(target):
                        return country
        
        if attacker_level in ["初级", "中级"]:
            src_ip_country = attacker_country
        else:
            src_ip_country = find_country(src_ip)

        # HTTP方法
        http_method = attacker.get_random_http_method()

        # Host
        host = random.choice(attacker.host_preferences)

        # 攻击类型
        attack_type=attacker.generate_attack_type(phase,phase_count,index)

        
        # URL
        if http_method=="GET":
            # url_path =  random.choice( attacker.geturl_preferenes)
            url_path =  random.choice( attacker.url_preferences)
        else:
            url_path =  random.choice( attacker.url_preferences)
            

        # User Agent
        user_agent = attacker.generate_user_agent(linshi_id, attack_type)

        # Accept Language
        accept_language = attacker.generate_accept_language()

        # Cookie
        cookie = attacker.generate_cookie(host, linshi_id,phase)

        # X-Forwarded-For
        x_forwarded_for = attacker.get_x_forwarded_for(src_ip)

        # 载荷位置
        payload_location = attacker.get_random_payload_location(http_method)

        # 生成载荷
        payload, raw_payload, url_encoded, unicode_encoded, base64_encoded, html_comment = attacker.generate_payload(phase,attack_type, payload_location)

        # Post Body
        post_body = attacker.generate_post_body(http_method, payload_location)

        # 将载荷插入到相应位置
        if payload_location == "URL":
            if '?' in url_path:
                url_path += f'&payload={payload}'
            else:
                url_path += f'?payload={payload}'
        elif payload_location == "User Agent":
            user_agent = f'{user_agent} {payload}'
        elif payload_location == "Accept Language":
            accept_language = f'{accept_language}, {payload}'
        elif payload_location == "Cookie":
            cookie = f'{cookie}; payload={payload}'
        elif payload_location == "X-Forwarded-For":
            if x_forwarded_for:
                x_forwarded_for = f'{x_forwarded_for}, {payload}'
            else:
                x_forwarded_for = payload
        elif payload_location == "Post Body" and post_body:
            if post_body.startswith('{') and post_body.endswith('}'):
                # JSON格式
                try:
                    json_data = json.loads(post_body)
                    json_data['payload'] = payload
                    post_body = json.dumps(json_data)
                except:
                    # 如果JSON解析失败，直接添加
                    post_body = f'{post_body[:-1]}, "payload": "{payload}"}}'
            else:
                # 表单格式
                post_body = f'{post_body}&payload={payload}'

        return {
            "序号": len(self.dataset) + 1,
            "真实攻击者": attacker_id,
            "攻击者水平": attacker_level,
            "真实攻击者所在国家": attacker_country,
            "攻击警报时间": attack_time.strftime("%Y-%m-%d %H:%M:%S"),
            "与上次攻击的时间间隔（秒）": time_diff,
            "攻击源ip地址": src_ip,
            "攻击源IP地址所在国家": src_ip_country,
            "HTTP method": http_method,
            "Host": host,
            "URL": url_path,
            "User Agent": user_agent,
            "Accept Language": accept_language,
            "Cookie": cookie,
            "X-Forwarded-For": x_forwarded_for,
            "Post Body": post_body,
            "攻击类型": attack_type,
            "攻击阶段": phase,
            "载荷": raw_payload,
            "攻击载荷位置": payload_location,
            "载荷是否使用了URL编码绕过": url_encoded,
            "载荷是否进行了Unicode实体编码": unicode_encoded,
            "载荷是否使用了Base64编码绕过": base64_encoded,
            "载荷是否进行了HTML注释绕过": html_comment
        }

    def save_dataset(self):
        # 保存数据集到CSV和TXT文件
        if not self.dataset:
            print("数据集为空，无法保存！")
            return

        # 保存为CSV
        df = pd.DataFrame(self.dataset)  # 正常数据集

        csv_path = "web_attack_alert_dataset.csv"
        df.to_csv(csv_path, index=False, encoding='utf-8-sig')
        print(f"数据集已保存到{csv_path}")

        df = pd.DataFrame(self.noise_dataset)   # 噪声数据集
        csv_path = "web_attack_alert_dataset_noise.csv"
        df.to_csv(csv_path, index=False, encoding='utf-8-sig')
        print(f"数据集已保存到{csv_path}")

        if  args:
            path=f"web_attack_alert_dataset_{args[0]}user_{args[1]}_{args[2]}.csv"
            df.to_csv(path, index=False, encoding='utf-8-sig')
            print(f"数据集已保存到{path}")

        # 保存为TXT（每行一个JSON对象）
        # txt_path = "web_attack_alert_dataset.txt"
        # with open(txt_path, 'w', encoding='utf-8') as f:
        #     for attack in self.dataset:
        #         f.write(json.dumps(attack, ensure_ascii=False, indent=2) + '\n')
        # print(f"数据集已保存到{txt_path}")

        # 保存攻击者行为偏好CSV
        # profile_df = pd.DataFrame(self.attacker_profiles)
        # profile_csv_path = "attacker_behavior_preferences.csv"
        # profile_df.to_csv(profile_csv_path, index=False, encoding='utf-8-sig')
        # print(f"攻击者行为偏好已保存到{profile_csv_path}")

    def save_dataset_withdetail(self,num_attackers,weights,basenum):
        # 保存数据集到CSV和TXT文件
        if not self.dataset:
            print("数据集为空，无法保存！")
            return

        # 保存为CSV
        df = pd.DataFrame(self.dataset)
        csv_path = f"web_attack_alert_dataset_{num_attackers}_{weights}_{basenum}.csv" 
        df.to_csv(csv_path, index=False, encoding='utf-8-sig')
        print(f"数据集已保存到{csv_path}")


if __name__ == "__main__":
    # 创建生成器
    args = sys.argv[1:]
    if args:
        num_attackers_t=int(args[0])
        weights_t=ast.literal_eval(args[1])
        basenum_t=int(args[2])
    else:
        num_attackers_t=10
        weights_t=[0.3, 0.5, 0.2]
        basenum_t=7

    generator = AttackDatasetGenerator(num_attackers_t,weights_t,basenum_t)  #

    # 生成数据集
    generator.generate_dataset(basenum_t)  #基础数量

    # 保存数据集
    generator.save_dataset()   #
    # generator.save_dataset_withdetail(num_attackers,weights,basenum)